blob: 7357520199a6805157d51ab83d4d2944c5da2153 [file] [log] [blame]
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * vim9class.c: Vim9 script class support
12 */
13
14#define USING_FLOAT_STUFF
15#include "vim.h"
16
17#if defined(FEAT_EVAL) || defined(PROTO)
18
19// When not generating protos this is included in proto.h
20#ifdef PROTO
21# include "vim9.h"
22#endif
23
Yegappan Lakshmanane651e112023-09-04 07:51:01 +020024static class_T *first_class = NULL;
25static class_T *next_nonref_class = NULL;
26
27/*
28 * Call this function when a class has been created. It will be added to the
29 * list headed by "first_class".
30 */
31 static void
32class_created(class_T *cl)
33{
34 if (first_class != NULL)
35 {
36 cl->class_next_used = first_class;
37 first_class->class_prev_used = cl;
38 }
39 first_class = cl;
40}
41
42/*
43 * Call this function when a class has been cleared and is about to be freed.
44 * It is removed from the list headed by "first_class".
45 */
46 static void
47class_cleared(class_T *cl)
48{
49 if (cl->class_next_used != NULL)
50 cl->class_next_used->class_prev_used = cl->class_prev_used;
51 if (cl->class_prev_used != NULL)
52 cl->class_prev_used->class_next_used = cl->class_next_used;
53 else if (first_class == cl)
54 first_class = cl->class_next_used;
55
56 // update the next class to check if needed
57 if (cl == next_nonref_class)
58 next_nonref_class = cl->class_next_used;
59}
60
Bram Moolenaarc1c365c2022-12-04 20:13:24 +000061/*
Bram Moolenaard505d172022-12-18 21:42:55 +000062 * Parse a member declaration, both object and class member.
63 * Returns OK or FAIL. When OK then "varname_end" is set to just after the
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +020064 * variable name and "type_ret" is set to the declared or detected type.
Bram Moolenaard505d172022-12-18 21:42:55 +000065 * "init_expr" is set to the initialisation expression (allocated), if there is
Bram Moolenaar554d0312023-01-05 19:59:18 +000066 * one. For an interface "init_expr" is NULL.
Bram Moolenaard505d172022-12-18 21:42:55 +000067 */
68 static int
69parse_member(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020070 exarg_T *eap,
71 char_u *line,
72 char_u *varname,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020073 int has_public, // TRUE if "public" seen before "varname"
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020074 char_u **varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +020075 int *has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020076 garray_T *type_list,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020077 type_T **type_ret,
78 char_u **init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +000079{
80 *varname_end = to_name_end(varname, FALSE);
81 if (*varname == '_' && has_public)
82 {
RestorerZ7fe8f432023-09-24 23:21:24 +020083 semsg(_(e_public_variable_name_cannot_start_with_underscore_str), line);
Bram Moolenaard505d172022-12-18 21:42:55 +000084 return FAIL;
85 }
86
87 char_u *colon = skipwhite(*varname_end);
88 char_u *type_arg = colon;
89 type_T *type = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +020090 *has_type = FALSE;
Bram Moolenaard505d172022-12-18 21:42:55 +000091 if (*colon == ':')
92 {
93 if (VIM_ISWHITE(**varname_end))
94 {
95 semsg(_(e_no_white_space_allowed_before_colon_str), varname);
96 return FAIL;
97 }
98 if (!VIM_ISWHITE(colon[1]))
99 {
100 semsg(_(e_white_space_required_after_str_str), ":", varname);
101 return FAIL;
102 }
103 type_arg = skipwhite(colon + 1);
104 type = parse_type(&type_arg, type_list, TRUE);
105 if (type == NULL)
106 return FAIL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200107 *has_type = TRUE;
Bram Moolenaard505d172022-12-18 21:42:55 +0000108 }
109
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200110 char_u *init_arg = skipwhite(type_arg);
111 if (type == NULL && *init_arg != '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000112 {
113 emsg(_(e_type_or_initialization_required));
114 return FAIL;
115 }
116
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200117 if (init_expr == NULL && *init_arg == '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000118 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200119 emsg(_(e_cannot_initialize_variable_in_interface));
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200120 return FAIL;
121 }
122
123 if (*init_arg == '=')
124 {
125 evalarg_T evalarg;
126 char_u *expr_start, *expr_end;
127
128 if (!VIM_ISWHITE(init_arg[-1]) || !VIM_ISWHITE(init_arg[1]))
Bram Moolenaard505d172022-12-18 21:42:55 +0000129 {
130 semsg(_(e_white_space_required_before_and_after_str_at_str),
131 "=", type_arg);
132 return FAIL;
133 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200134 init_arg = skipwhite(init_arg + 1);
Bram Moolenaard505d172022-12-18 21:42:55 +0000135
Bram Moolenaard505d172022-12-18 21:42:55 +0000136 fill_evalarg_from_eap(&evalarg, eap, FALSE);
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200137 (void)skip_expr_concatenate(&init_arg, &expr_start, &expr_end, &evalarg);
Bram Moolenaard505d172022-12-18 21:42:55 +0000138
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +0200139 // No type specified for the member. Set it to "any" and the correct
140 // type will be set when the object is instantiated.
Bram Moolenaard505d172022-12-18 21:42:55 +0000141 if (type == NULL)
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200142 type = &t_any;
Bram Moolenaard505d172022-12-18 21:42:55 +0000143
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200144 *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
145 // Free the memory pointed by expr_start.
Bram Moolenaard505d172022-12-18 21:42:55 +0000146 clear_evalarg(&evalarg, NULL);
147 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200148 else if (!valid_declaration_type(type))
Bram Moolenaard505d172022-12-18 21:42:55 +0000149 return FAIL;
150
151 *type_ret = type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000152 return OK;
153}
154
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100155typedef struct oc_newmember_S oc_newmember_T;
156struct oc_newmember_S
157{
158 garray_T *gap;
159 char_u *varname;
160 char_u *varname_end;
161 int has_public;
162 int has_final;
163 int has_type;
164 type_T *type;
165 char_u *init_expr;
166};
167
Bram Moolenaard505d172022-12-18 21:42:55 +0000168/*
169 * Add a member to an object or a class.
170 * Returns OK when successful, "init_expr" will be consumed then.
171 * Returns FAIL otherwise, caller might need to free "init_expr".
172 */
173 static int
174add_member(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200175 garray_T *gap,
176 char_u *varname,
177 char_u *varname_end,
178 int has_public,
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100179 int has_final,
180 int has_const,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200181 int has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200182 type_T *type,
183 char_u *init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +0000184{
185 if (ga_grow(gap, 1) == FAIL)
186 return FAIL;
187 ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
188 m->ocm_name = vim_strnsave(varname, varname_end - varname);
=?UTF-8?q?Ola=20S=C3=B6der?=d8742472023-03-05 13:12:32 +0000189 m->ocm_access = has_public ? VIM_ACCESS_ALL
190 : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100191 if (has_final)
192 m->ocm_flags |= OCMFLAG_FINAL;
193 if (has_const)
194 m->ocm_flags |= OCMFLAG_CONST;
195 if (has_type)
196 m->ocm_flags |= OCMFLAG_HAS_TYPE;
Bram Moolenaard505d172022-12-18 21:42:55 +0000197 m->ocm_type = type;
198 if (init_expr != NULL)
199 m->ocm_init = init_expr;
200 ++gap->ga_len;
201 return OK;
202}
203
204/*
205 * Move the class or object members found while parsing a class into the class.
206 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000207 * "parent_members" points to the members in the parent class (if any)
208 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000209 * "members" will be set to the newly allocated array of members and
210 * "member_count" set to the number of members.
211 * Returns OK or FAIL.
212 */
213 static int
214add_members_to_class(
215 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000216 ocmember_T *parent_members,
217 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000218 ocmember_T **members,
219 int *member_count)
220{
Bram Moolenaar83677162023-01-08 19:54:10 +0000221 *member_count = parent_count + gap->ga_len;
222 *members = *member_count == 0 ? NULL
223 : ALLOC_MULT(ocmember_T, *member_count);
224 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000225 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000226 for (int i = 0; i < parent_count; ++i)
227 {
228 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000229 ocmember_T *m = *members + i;
230 *m = parent_members[i];
231 m->ocm_name = vim_strsave(m->ocm_name);
232 if (m->ocm_init != NULL)
233 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000234 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000235 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000236 // new members are moved
237 mch_memmove(*members + parent_count,
238 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000239 VIM_CLEAR(gap->ga_data);
240 return OK;
241}
242
243/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000244 * Convert a member index "idx" of interface "itf" to the member index of class
245 * "cl" implementing that interface.
246 */
247 int
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200248object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000249{
Ernie Rael18143d32023-09-04 22:30:41 +0200250 if (idx >= (is_method ? itf->class_obj_method_count
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200251 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000252 {
253 siemsg("index %d out of range for interface %s", idx, itf->class_name);
254 return 0;
255 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200256
257 // If "cl" is the interface or the class that is extended, then the method
258 // index can be used directly and there is no need to search for the method
259 // index in one of the child classes.
260 if (cl == itf)
261 return idx;
262
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200263 itf2class_T *i2c = NULL;
264 int searching = TRUE;
265 int method_offset = 0;
266
Ernie Raelcf138d42023-09-06 20:45:03 +0200267 for (class_T *super = cl; super != NULL && searching;
268 super = super->class_extends)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200269 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200270 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200271 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200272 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
273 {
274 searching = FALSE;
275 break;
276 }
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200277 }
278 if (searching && is_method)
279 // The parent class methods are stored after the current class
280 // methods.
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200281 method_offset += super->class_obj_method_count_child;
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200282 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000283 if (i2c == NULL)
284 {
285 siemsg("class %s not found on interface %s",
286 cl->class_name, itf->class_name);
287 return 0;
288 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +0200289
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200290 // A table follows the i2c for the class
291 int *table = (int *)(i2c + 1);
292 // "method_offset" is 0, if method is in the current class. If method
293 // is in a parent class, then it is non-zero.
294 return table[idx] + method_offset;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000295}
296
297/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200298 * Check whether a class named "extends_name" is present. If the class is
299 * valid, then "extends_clp" is set with the class pointer.
300 * Returns TRUE if the class name "extends_names" is a valid class.
301 */
302 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200303validate_extends_class(
304 char_u *extends_name,
305 class_T **extends_clp,
306 int is_class)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200307{
308 typval_T tv;
309 int success = FALSE;
310
311 tv.v_type = VAR_UNKNOWN;
312 if (eval_variable_import(extends_name, &tv) == FAIL)
313 {
314 semsg(_(e_class_name_not_found_str), extends_name);
315 return success;
316 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200317
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200318 if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
319 || (is_class
320 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
321 || (!is_class
322 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0))
323 // a interface cannot extend a class and a class cannot extend an
324 // interface.
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200325 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200326 else
327 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200328 class_T *extends_cl = tv.vval.v_class;
329 ++extends_cl->class_refcount;
330 *extends_clp = extends_cl;
331 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200332 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200333 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200334
335 return success;
336}
337
338/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200339 * Check method names in the parent class lineage to make sure the access is
340 * the same for overridden methods.
341 */
342 static int
343validate_extends_methods(
344 garray_T *objmethods_gap,
345 class_T *extends_cl)
346{
347 class_T *super = extends_cl;
348 int method_count = objmethods_gap->ga_len;
349 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
350
351 while (super != NULL)
352 {
353 int extends_method_count = super->class_obj_method_count_child;
354 if (extends_method_count == 0)
355 {
356 super = super->class_extends;
357 continue;
358 }
359
360 ufunc_T **extends_methods = super->class_obj_methods;
361
362 for (int i = 0; i < extends_method_count; i++)
363 {
364 char_u *pstr = extends_methods[i]->uf_name;
365 int extends_private = (*pstr == '_');
366 if (extends_private)
367 pstr++;
368
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200369 // When comparing the method names, ignore the access type (public
370 // and private methods are considered the same).
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200371 for (int j = 0; j < method_count; j++)
372 {
373 char_u *qstr = cl_fp[j]->uf_name;
374 int priv_method = (*qstr == '_');
375 if (priv_method)
376 qstr++;
377 if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
378 {
379 // Method access is different between the super class and
380 // the subclass
381 semsg(_(e_method_str_of_class_str_has_different_access),
382 cl_fp[j]->uf_name, super->class_name);
383 return FALSE;
384 }
385 }
386 }
387 super = super->class_extends;
388 }
389
390 return TRUE;
391}
392
393/*
394 * Check whether a object member variable in "objmembers_gap" is a duplicate of
395 * a member in any of the extended parent class lineage. Returns TRUE if there
396 * are no duplicates.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200397 */
398 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200399extends_check_dup_members(
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200400 garray_T *objmembers_gap,
401 class_T *extends_cl)
402{
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200403 int member_count = objmembers_gap->ga_len;
404 if (member_count == 0)
405 return TRUE;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200406
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200407 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
408
409 // Validate each member variable
410 for (int c_i = 0; c_i < member_count; c_i++)
411 {
412 class_T *p_cl = extends_cl;
413 ocmember_T *c_m = members + c_i;
414 char_u *pstr = (*c_m->ocm_name == '_')
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200415 ? c_m->ocm_name + 1 : c_m->ocm_name;
416
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200417 // Check in all the parent classes in the lineage
418 while (p_cl != NULL)
419 {
420 int p_member_count = p_cl->class_obj_member_count;
421 if (p_member_count == 0)
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200422 {
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200423 p_cl = p_cl->class_extends;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200424 continue;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200425 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200426 ocmember_T *p_members = p_cl->class_obj_members;
427
428 // Compare against all the members in the parent class
429 for (int p_i = 0; p_i < p_member_count; p_i++)
430 {
431 ocmember_T *p_m = p_members + p_i;
432 char_u *qstr = (*p_m->ocm_name == '_')
433 ? p_m->ocm_name + 1 : p_m->ocm_name;
434 if (STRCMP(pstr, qstr) == 0)
435 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200436 semsg(_(e_duplicate_variable_str), c_m->ocm_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200437 return FALSE;
438 }
439 }
440
441 p_cl = p_cl->class_extends;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200442 }
443 }
444
445 return TRUE;
446}
447
448/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200449 * Compare the variable type of interface variables in "objmembers_gap" against
450 * the variable in any of the extended super interface lineage. Used to
451 * compare the variable types when extending interfaces. Returns TRUE if the
452 * variable types are the same.
453 */
454 static int
455extends_check_intf_var_type(
456 garray_T *objmembers_gap,
457 class_T *extends_cl)
458{
459 int member_count = objmembers_gap->ga_len;
460 if (member_count == 0)
461 return TRUE;
462
463 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
464
465 // Validate each member variable
466 for (int c_i = 0; c_i < member_count; c_i++)
467 {
468 class_T *p_cl = extends_cl;
469 ocmember_T *c_m = members + c_i;
470 int var_found = FALSE;
471
472 // Check in all the parent classes in the lineage
473 while (p_cl != NULL && !var_found)
474 {
475 int p_member_count = p_cl->class_obj_member_count;
476 if (p_member_count == 0)
477 {
478 p_cl = p_cl->class_extends;
479 continue;
480 }
481 ocmember_T *p_members = p_cl->class_obj_members;
482
483 // Compare against all the members in the parent class
484 for (int p_i = 0; p_i < p_member_count; p_i++)
485 {
486 where_T where = WHERE_INIT;
487 ocmember_T *p_m = p_members + p_i;
488
489 if (STRCMP(p_m->ocm_name, c_m->ocm_name) != 0)
490 continue;
491
492 // Ensure the type is matching.
493 where.wt_func_name = (char *)c_m->ocm_name;
494 where.wt_kind = WT_MEMBER;
495
496 if (check_type(p_m->ocm_type, c_m->ocm_type, TRUE,
497 where) == FAIL)
498 return FALSE;
499
500 var_found = TRUE;
501 }
502
503 p_cl = p_cl->class_extends;
504 }
505 }
506
507 return TRUE;
508}
509
510/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200511 * When extending an abstract class, check whether all the abstract methods in
512 * the parent class are implemented. Returns TRUE if all the methods are
513 * implemented.
514 */
515 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200516validate_abstract_class_methods(
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200517 garray_T *classmethods_gap,
518 garray_T *objmethods_gap,
519 class_T *extends_cl)
520{
521 for (int loop = 1; loop <= 2; ++loop)
522 {
523 // loop == 1: check class methods
524 // loop == 2: check object methods
525 int extends_method_count = loop == 1
526 ? extends_cl->class_class_function_count
527 : extends_cl->class_obj_method_count;
528 if (extends_method_count == 0)
529 continue;
530
531 ufunc_T **extends_methods = loop == 1
532 ? extends_cl->class_class_functions
533 : extends_cl->class_obj_methods;
534
535 int method_count = loop == 1 ? classmethods_gap->ga_len
536 : objmethods_gap->ga_len;
537 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
538 ? classmethods_gap->ga_data
539 : objmethods_gap->ga_data);
540
541 for (int i = 0; i < extends_method_count; i++)
542 {
543 ufunc_T *uf = extends_methods[i];
Yegappan Lakshmanan1db15142023-09-19 20:34:05 +0200544 if (!IS_ABSTRACT_METHOD(uf))
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200545 continue;
546
547 int method_found = FALSE;
548
549 for (int j = 0; j < method_count; j++)
550 {
551 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
552 {
553 method_found = TRUE;
554 break;
555 }
556 }
557
558 if (!method_found)
559 {
560 semsg(_(e_abstract_method_str_not_found), uf->uf_name);
561 return FALSE;
562 }
563 }
564 }
565
566 return TRUE;
567}
568
569/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200570 * Returns TRUE if the interface variable "if_var" is present in the list of
571 * variables in "cl_mt" or in the parent lineage of one of the extended classes
572 * in "extends_cl". For a class variable, 'is_class_var' is TRUE.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200573 */
574 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200575intf_variable_present(
576 char_u *intf_class_name,
577 ocmember_T *if_var,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200578 ocmember_T *cl_mt,
579 int cl_member_count,
580 class_T *extends_cl)
581{
582 int variable_present = FALSE;
583
584 for (int cl_i = 0; cl_i < cl_member_count; ++cl_i)
585 {
586 ocmember_T *m = &cl_mt[cl_i];
587 where_T where = WHERE_INIT;
588
589 if (STRCMP(if_var->ocm_name, m->ocm_name) != 0)
590 continue;
591
592 // Ensure the access type is same
593 if (if_var->ocm_access != m->ocm_access)
594 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200595 semsg(_(e_variable_str_of_interface_str_has_different_access),
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200596 if_var->ocm_name, intf_class_name);
597 return FALSE;
598 }
599
600 // Ensure the type is matching.
601 if (m->ocm_type == &t_any)
602 {
603 // variable type is not specified. Use the variable type in the
604 // interface.
605 m->ocm_type = if_var->ocm_type;
606 }
607 else
608 {
609 where.wt_func_name = (char *)m->ocm_name;
610 where.wt_kind = WT_MEMBER;
611 if (check_type(if_var->ocm_type, m->ocm_type, TRUE,
612 where) == FAIL)
613 return FALSE;
614 }
615
616 variable_present = TRUE;
617 break;
618 }
619
620 if (!variable_present && extends_cl != NULL)
621 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200622 int ext_cl_count = extends_cl->class_obj_member_count;
623 ocmember_T *ext_cl_mt = extends_cl->class_obj_members;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200624 return intf_variable_present(intf_class_name, if_var,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200625 ext_cl_mt, ext_cl_count,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200626 extends_cl->class_extends);
627 }
628
629 return variable_present;
630}
631
632/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200633 * Check the variables of the interface class "ifcl" match object variables
634 * ("objmembers_gap") of a class.
635 * Returns TRUE if the object variables names are valid.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200636 */
637 static int
638validate_interface_variables(
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200639 char_u *intf_class_name,
640 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200641 garray_T *objmembers_gap,
642 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200643{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200644 int if_count = ifcl->class_obj_member_count;
645 if (if_count == 0)
646 return TRUE;
647
648 ocmember_T *if_ms = ifcl->class_obj_members;
649 ocmember_T *cl_ms = (ocmember_T *)(objmembers_gap->ga_data);
650 int cl_count = objmembers_gap->ga_len;
651 for (int if_i = 0; if_i < if_count; ++if_i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200652 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200653 if (!intf_variable_present(intf_class_name, &if_ms[if_i], cl_ms,
654 cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200655 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200656 semsg(_(e_variable_str_of_interface_str_not_implemented),
657 if_ms[if_i].ocm_name, intf_class_name);
658 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200659 }
660 }
661
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200662 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200663}
664
665/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200666 * Returns TRUE if the method signature of "if_method" and "cl_method" matches.
667 */
668 static int
669intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method)
670{
671 where_T where = WHERE_INIT;
672
673 // Ensure the type is matching.
674 where.wt_func_name = (char *)if_method->uf_name;
675 where.wt_kind = WT_METHOD;
676 if (check_type(if_method->uf_func_type, cl_method->uf_func_type, TRUE,
677 where) == FAIL)
678 return FALSE;
679
680 return TRUE;
681}
682
683/*
684 * Returns TRUE if the interface method "if_ufunc" is present in the list of
685 * methods in "cl_fp" or in the parent lineage of one of the extended classes
686 * in "extends_cl". For a class method, 'is_class_method' is TRUE.
687 */
688 static int
689intf_method_present(
690 ufunc_T *if_ufunc,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200691 ufunc_T **cl_fp,
692 int cl_count,
693 class_T *extends_cl)
694{
695 int method_present = FALSE;
696
697 for (int cl_i = 0; cl_i < cl_count; ++cl_i)
698 {
699 char_u *cl_name = cl_fp[cl_i]->uf_name;
700 if (STRCMP(if_ufunc->uf_name, cl_name) == 0)
701 {
702 // Ensure the type is matching.
703 if (!intf_method_type_matches(if_ufunc, cl_fp[cl_i]))
704 return FALSE;
705 method_present = TRUE;
706 break;
707 }
708 }
709
710 if (!method_present && extends_cl != NULL)
711 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200712 ufunc_T **ext_cl_fp = (ufunc_T **)(extends_cl->class_obj_methods);
713 int ext_cl_count = extends_cl->class_obj_method_count;
714 return intf_method_present(if_ufunc, ext_cl_fp, ext_cl_count,
715 extends_cl->class_extends);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200716 }
717
718 return method_present;
719}
720
721/*
722 * Validate that a new class implements all the class/instance methods in the
723 * interface "ifcl". The new class methods are in "classfunctions_gap" and the
724 * new object methods are in "objmemthods_gap". Also validates the method
725 * types.
726 * Returns TRUE if all the interface class/object methods are implemented in
727 * the new class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200728 */
729 static int
730validate_interface_methods(
731 char_u *intf_class_name,
732 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200733 garray_T *objmethods_gap,
734 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200735{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200736 int if_count = ifcl->class_obj_method_count;
737 if (if_count == 0)
738 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200739
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200740 ufunc_T **if_fp = ifcl->class_obj_methods;
741 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
742 int cl_count = objmethods_gap->ga_len;
743 for (int if_i = 0; if_i < if_count; ++if_i)
744 {
745 char_u *if_name = if_fp[if_i]->uf_name;
746
747 if (!intf_method_present(if_fp[if_i], cl_fp, cl_count, extends_cl))
748 {
749 semsg(_(e_method_str_of_interface_str_not_implemented),
750 if_name, intf_class_name);
751 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200752 }
753 }
754
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200755 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200756}
757
758/*
759 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200760 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200761 * object methods and object members in the new class are in
762 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
763 * "objmembers_gap" respectively.
764 */
765 static int
766validate_implements_classes(
767 garray_T *impl_gap,
768 class_T **intf_classes,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200769 garray_T *objmethods_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200770 garray_T *objmembers_gap,
771 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200772{
773 int success = TRUE;
774
775 for (int i = 0; i < impl_gap->ga_len && success; ++i)
776 {
777 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
778 typval_T tv;
779 tv.v_type = VAR_UNKNOWN;
780 if (eval_variable_import(impl, &tv) == FAIL)
781 {
782 semsg(_(e_interface_name_not_found_str), impl);
783 success = FALSE;
784 break;
785 }
786
787 if (tv.v_type != VAR_CLASS
788 || tv.vval.v_class == NULL
789 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
790 {
791 semsg(_(e_not_valid_interface_str), impl);
792 success = FALSE;
793 clear_tv(&tv);
794 break;
795 }
796
797 class_T *ifcl = tv.vval.v_class;
798 intf_classes[i] = ifcl;
799 ++ifcl->class_refcount;
800
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200801 // check the variables of the interface match the members of the class
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200802 success = validate_interface_variables(impl, ifcl, objmembers_gap,
803 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200804
805 // check the functions/methods of the interface match the
806 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200807 if (success)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200808 success = validate_interface_methods(impl, ifcl, objmethods_gap,
809 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200810 clear_tv(&tv);
811 }
812
813 return success;
814}
815
816/*
817 * Check no function argument name is used as a class member.
818 * (Object members are always accessed with "this." prefix, so no need
819 * to check them.)
820 */
821 static int
822check_func_arg_names(
823 garray_T *classfunctions_gap,
824 garray_T *objmethods_gap,
825 garray_T *classmembers_gap)
826{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200827 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200828 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200829 {
830 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
831
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200832 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200833 {
834 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
835
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200836 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200837 {
838 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
839 garray_T *mgap = classmembers_gap;
840
841 // Check all the class member names
842 for (int mi = 0; mi < mgap->ga_len; ++mi)
843 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200844 char_u *mname =
845 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200846 if (STRCMP(aname, mname) == 0)
847 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200848 if (uf->uf_script_ctx.sc_sid > 0)
849 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
850
851 semsg(_(e_argument_already_declared_in_class_str),
852 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200853
854 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200855 }
856 }
857 }
858 }
859 }
860
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200861 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200862}
863
864/*
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200865 * Returns TRUE if 'varname' is a reserved keyword name
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200866 */
867 static int
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200868is_reserved_varname(char_u *varname, char_u *varname_end)
869{
870 int reserved = FALSE;
871 char_u save_varname_end = *varname_end;
872 *varname_end = NUL;
873
874 reserved = check_reserved_name(varname, FALSE) == FAIL;
875
876 *varname_end = save_varname_end;
877
878 return reserved;
879}
880
881/*
882 * Returns TRUE if the variable "varname" is already defined either as a class
883 * variable or as an object variable.
884 */
885 static int
886is_duplicate_variable(
887 garray_T *class_members,
888 garray_T *obj_members,
889 char_u *varname,
890 char_u *varname_end)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200891{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200892 char_u *name = vim_strnsave(varname, varname_end - varname);
893 char_u *pstr = (*name == '_') ? name + 1 : name;
894 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200895
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200896 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200897 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200898 // loop == 1: class variables, loop == 2: object variables
899 garray_T *vgap = (loop == 1) ? class_members : obj_members;
900 for (int i = 0; i < vgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200901 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200902 ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i;
903 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1
904 : m->ocm_name;
905 if (STRCMP(pstr, qstr) == 0)
906 {
907 semsg(_(e_duplicate_variable_str), name);
908 dup = TRUE;
909 break;
910 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200911 }
912 }
913
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200914 vim_free(name);
915 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200916}
917
918/*
919 * Returns TRUE if the method "name" is already defined.
920 */
921 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200922is_duplicate_method(
923 garray_T *classmethods_gap,
924 garray_T *objmethods_gap,
925 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200926{
927 char_u *pstr = (*name == '_') ? name + 1 : name;
928
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200929 // loop 1: class methods, loop 2: object methods
930 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200931 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200932 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
933 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200934 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200935 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
936 char_u *qstr = *n == '_' ? n + 1 : n;
937 if (STRCMP(pstr, qstr) == 0)
938 {
939 semsg(_(e_duplicate_function_str), name);
940 return TRUE;
941 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200942 }
943 }
944
945 return FALSE;
946}
947
948/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200949 * Returns TRUE if the constructor is valid.
950 */
951 static int
952is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
953{
954 // Constructors are not allowed in abstract classes.
955 if (is_abstract)
956 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200957 emsg(_(e_cannot_define_new_method_in_abstract_class));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200958 return FALSE;
959 }
960 // A constructor is always static, no need to define it so.
961 if (has_static)
962 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200963 emsg(_(e_cannot_define_new_method_as_static));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200964 return FALSE;
965 }
966 // A return type should not be specified for the new()
967 // constructor method.
968 if (uf->uf_ret_type->tt_type != VAR_VOID)
969 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200970 emsg(_(e_cannot_use_a_return_type_with_new_method));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200971 return FALSE;
972 }
973 return TRUE;
974}
975
976/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +0100977 * Returns TRUE if 'uf' is a supported builtin method and has the correct
978 * method signature.
979 */
980 static int
981object_check_builtin_method_sig(ufunc_T *uf)
982{
983 char_u *name = uf->uf_name;
984 int valid = FALSE;
985 type_T method_sig;
986 type_T method_rt;
987 where_T where = WHERE_INIT;
988
989 // validate the method signature
990 CLEAR_FIELD(method_sig);
991 CLEAR_FIELD(method_rt);
992 method_sig.tt_type = VAR_FUNC;
993
994 if (STRCMP(name, "len") == 0)
995 {
996 // def __len(): number
997 method_rt.tt_type = VAR_NUMBER;
998 method_sig.tt_member = &method_rt;
999 valid = TRUE;
1000 }
1001 else if (STRCMP(name, "empty") == 0)
1002 {
1003 // def __empty(): bool
1004 method_rt.tt_type = VAR_BOOL;
1005 method_sig.tt_member = &method_rt;
1006 valid = TRUE;
1007 }
1008 else if (STRCMP(name, "string") == 0)
1009 {
1010 // def __string(): string
1011 method_rt.tt_type = VAR_STRING;
1012 method_sig.tt_member = &method_rt;
1013 valid = TRUE;
1014 }
1015 else
1016 semsg(_(e_builtin_object_method_str_not_supported), uf->uf_name);
1017
1018 where.wt_func_name = (char *)uf->uf_name;
1019 where.wt_kind = WT_METHOD;
1020 if (valid && !check_type(&method_sig, uf->uf_func_type, TRUE, where))
1021 valid = FALSE;
1022
1023 return valid;
1024}
1025
1026/*
1027 * Returns TRUE if "funcname" is a supported builtin object method name
1028 */
1029 int
1030is_valid_builtin_obj_methodname(char_u *funcname)
1031{
1032 switch (funcname[0])
1033 {
1034 case 'e':
1035 return STRNCMP(funcname, "empty", 5) == 0;
1036
1037 case 'l':
1038 return STRNCMP(funcname, "len", 3) == 0;
1039
1040 case 'n':
1041 return STRNCMP(funcname, "new", 3) == 0;
1042
1043 case 's':
1044 return STRNCMP(funcname, "string", 6) == 0;
1045 }
1046
1047 return FALSE;
1048}
1049
1050
1051/*
1052 * Returns the builtin method "name" in object "obj". Returns NULL if the
1053 * method is not found.
1054 */
1055 ufunc_T *
1056class_get_builtin_method(
1057 class_T *cl,
1058 class_builtin_T builtin_method,
1059 int *method_idx)
1060{
1061 *method_idx = -1;
1062
1063 if (cl == NULL)
1064 return NULL;
1065
1066 *method_idx = cl->class_builtin_methods[builtin_method];
1067 return *method_idx != -1 ? cl->class_obj_methods[*method_idx] : NULL;
1068}
1069
1070/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001071 * Update the interface class lookup table for the member index on the
1072 * interface to the member index in the class implementing the interface.
1073 * And a lookup table for the object method index on the interface
1074 * to the object method index in the class implementing the interface.
1075 * This is also used for updating the lookup table for the extended class
1076 * hierarchy.
1077 */
1078 static int
1079update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001080 class_T *ifcl,
1081 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001082 garray_T *objmethods,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001083 int pobj_method_offset)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001084{
1085 if (ifcl == NULL)
1086 return OK;
1087
1088 // Table for members.
1089 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001090 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001091 if (if2cl == NULL)
1092 return FAIL;
1093 if2cl->i2c_next = ifcl->class_itf2class;
1094 ifcl->class_itf2class = if2cl;
1095 if2cl->i2c_class = cl;
1096 if2cl->i2c_is_method = FALSE;
1097
1098 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
1099 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
1100 {
1101 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001102 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001103 {
1104 int *table = (int *)(if2cl + 1);
1105 table[if_i] = cl_i;
1106 break;
1107 }
1108 }
1109
1110 // Table for methods.
1111 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001112 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001113 if (if2cl == NULL)
1114 return FAIL;
1115 if2cl->i2c_next = ifcl->class_itf2class;
1116 ifcl->class_itf2class = if2cl;
1117 if2cl->i2c_class = cl;
1118 if2cl->i2c_is_method = TRUE;
1119
1120 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
1121 {
1122 int done = FALSE;
1123 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
1124 {
1125 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001126 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001127 {
1128 int *table = (int *)(if2cl + 1);
1129 table[if_i] = cl_i;
1130 done = TRUE;
1131 break;
1132 }
1133 }
1134
1135 // extended class object method is not overridden by the child class.
1136 // Keep the method declared in one of the parent classes in the
1137 // lineage.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001138 if (!done)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001139 {
1140 // If "ifcl" is not the immediate parent of "cl", then search in
1141 // the intermediate parent classes.
1142 if (cl->class_extends != ifcl)
1143 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001144 class_T *parent = cl->class_extends;
1145 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001146
1147 while (!done && parent != NULL && parent != ifcl)
1148 {
1149
1150 for (int cl_i = 0;
1151 cl_i < parent->class_obj_method_count_child; ++cl_i)
1152 {
1153 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
1154 parent->class_obj_methods[cl_i]->uf_name)
1155 == 0)
1156 {
1157 int *table = (int *)(if2cl + 1);
1158 table[if_i] = method_offset + cl_i;
1159 done = TRUE;
1160 break;
1161 }
1162 }
1163 method_offset += parent->class_obj_method_count_child;
1164 parent = parent->class_extends;
1165 }
1166 }
1167
1168 if (!done)
1169 {
1170 int *table = (int *)(if2cl + 1);
1171 table[if_i] = pobj_method_offset + if_i;
1172 }
1173 }
1174 }
1175
1176 return OK;
1177}
1178
1179/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001180 * Update the member and object method lookup tables for a new class in the
1181 * interface class.
1182 * For each interface add a lookup table for the member index on the interface
1183 * to the member index in the new class. And a lookup table for the object
1184 * method index on the interface to the object method index in the new class.
1185 */
1186 static int
1187add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
1188{
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001189 // update the lookup table for all the implemented interfaces
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001190 for (int i = 0; i < cl->class_interface_count; ++i)
1191 {
1192 class_T *ifcl = cl->class_interfaces_cl[i];
1193
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001194 // update the lookup table for this interface and all its super
1195 // interfaces.
1196 while (ifcl != NULL)
1197 {
1198 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
1199 0) == FAIL)
1200 return FAIL;
1201 ifcl = ifcl->class_extends;
1202 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001203 }
1204
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001205 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001206 if (extends_cl != NULL)
1207 {
1208 class_T *pclass = extends_cl;
1209 int pobj_method_offset = objmethods_gap->ga_len;
1210
1211 // Update the entire lineage of extended classes.
1212 while (pclass != NULL)
1213 {
1214 if (update_member_method_lookup_table(pclass, cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001215 objmethods_gap, pobj_method_offset) == FAIL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001216 return FAIL;
1217
1218 pobj_method_offset += pclass->class_obj_method_count_child;
1219 pclass = pclass->class_extends;
1220 }
1221 }
1222
1223 return OK;
1224}
1225
1226/*
1227 * Add class members to a new class. Allocate a typval for each class member
1228 * and initialize it.
1229 */
1230 static void
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001231add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001232{
1233 // Allocate a typval for each class member and initialize it.
1234 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
1235 cl->class_class_member_count);
1236 if (cl->class_members_tv == NULL)
1237 return;
1238
1239 for (int i = 0; i < cl->class_class_member_count; ++i)
1240 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001241 ocmember_T *m = &cl->class_class_members[i];
1242 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001243 if (m->ocm_init != NULL)
1244 {
1245 typval_T *etv = eval_expr(m->ocm_init, eap);
1246 if (etv != NULL)
1247 {
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001248 if (m->ocm_type->tt_type == VAR_ANY
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001249 && !(m->ocm_flags & OCMFLAG_HAS_TYPE)
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001250 && etv->v_type != VAR_SPECIAL)
1251 // If the member variable type is not yet set, then use
1252 // the initialization expression type.
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001253 m->ocm_type = typval2type(etv, get_copyID(),
1254 type_list_gap,
1255 TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001256 *tv = *etv;
1257 vim_free(etv);
1258 }
1259 }
1260 else
1261 {
1262 // TODO: proper default value
1263 tv->v_type = m->ocm_type->tt_type;
1264 tv->vval.v_string = NULL;
1265 }
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001266 if (m->ocm_flags & OCMFLAG_CONST)
1267 item_lock(tv, DICT_MAXNEST, TRUE, TRUE);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001268 }
1269}
1270
1271/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001272 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001273 */
1274 static void
1275add_default_constructor(
1276 class_T *cl,
1277 garray_T *classfunctions_gap,
1278 garray_T *type_list_gap)
1279{
1280 garray_T fga;
1281
1282 ga_init2(&fga, 1, 1000);
1283 ga_concat(&fga, (char_u *)"new(");
1284 for (int i = 0; i < cl->class_obj_member_count; ++i)
1285 {
1286 if (i > 0)
1287 ga_concat(&fga, (char_u *)", ");
1288 ga_concat(&fga, (char_u *)"this.");
1289 ocmember_T *m = cl->class_obj_members + i;
1290 ga_concat(&fga, (char_u *)m->ocm_name);
1291 ga_concat(&fga, (char_u *)" = v:none");
1292 }
1293 ga_concat(&fga, (char_u *)")\nenddef\n");
1294 ga_append(&fga, NUL);
1295
1296 exarg_T fea;
1297 CLEAR_FIELD(fea);
1298 fea.cmdidx = CMD_def;
1299 fea.cmd = fea.arg = fga.ga_data;
1300
1301 garray_T lines_to_free;
1302 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1303
h-eastb895b0f2023-09-24 15:46:31 +02001304 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
1305 cl->class_obj_members, cl->class_obj_member_count);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001306
1307 ga_clear_strings(&lines_to_free);
1308 vim_free(fga.ga_data);
1309
1310 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1311 {
1312 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001313 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001314 ++classfunctions_gap->ga_len;
1315
1316 nf->uf_flags |= FC_NEW;
1317 nf->uf_ret_type = get_type_ptr(type_list_gap);
1318 if (nf->uf_ret_type != NULL)
1319 {
1320 nf->uf_ret_type->tt_type = VAR_OBJECT;
1321 nf->uf_ret_type->tt_class = cl;
1322 nf->uf_ret_type->tt_argcount = 0;
1323 nf->uf_ret_type->tt_args = NULL;
1324 }
1325 }
1326}
1327
1328/*
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001329 * Add the class methods and object methods to the new class "cl".
1330 * When extending a class "extends_cl", add the instance methods from the
1331 * parent class also.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001332 */
1333 static int
1334add_classfuncs_objmethods(
1335 class_T *cl,
1336 class_T *extends_cl,
1337 garray_T *classfunctions_gap,
1338 garray_T *objmethods_gap)
1339{
1340 // loop 1: class functions, loop 2: object methods
1341 for (int loop = 1; loop <= 2; ++loop)
1342 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001343 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1344 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001345 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001346 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001347 : &cl->class_obj_methods;
1348
1349 int parent_count = 0;
1350 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001351 // Include object methods from the parent.
1352 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001353 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001354 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001355 : extends_cl->class_obj_method_count;
1356
1357 *fcount = parent_count + gap->ga_len;
1358 if (*fcount == 0)
1359 {
1360 *fup = NULL;
1361 continue;
1362 }
1363 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1364 if (*fup == NULL)
1365 return FAIL;
1366
1367 if (gap->ga_len != 0)
1368 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
1369 vim_free(gap->ga_data);
1370 if (loop == 1)
1371 cl->class_class_function_count_child = gap->ga_len;
1372 else
1373 cl->class_obj_method_count_child = gap->ga_len;
1374
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001375 if (loop == 2)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001376 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001377 // Copy instance methods from the parent.
1378
1379 for (int i = 0; i < parent_count; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001380 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001381 // Can't use the same parent function, because "uf_class" is
1382 // different and compilation will have a different result.
1383 // Put them after the functions in the current class, object
1384 // methods may be overruled, then "super.Method()" is used to
1385 // find a method from the parent.
1386 ufunc_T *pf = (extends_cl->class_obj_methods)[i];
1387 (*fup)[gap->ga_len + i] = copy_function(pf);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001388
1389 // If the child class overrides a function from the parent
1390 // the signature must be equal.
1391 char_u *pname = pf->uf_name;
1392 for (int ci = 0; ci < gap->ga_len; ++ci)
1393 {
1394 ufunc_T *cf = (*fup)[ci];
1395 char_u *cname = cf->uf_name;
1396 if (STRCMP(pname, cname) == 0)
1397 {
1398 where_T where = WHERE_INIT;
1399 where.wt_func_name = (char *)pname;
1400 where.wt_kind = WT_METHOD;
1401 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1402 TRUE, where);
1403 }
1404 }
1405 }
1406 }
1407
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001408 // Set the class pointer on all the functions and object methods.
1409 for (int i = 0; i < *fcount; ++i)
1410 {
1411 ufunc_T *fp = (*fup)[i];
1412 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001413 if (i < gap->ga_len)
1414 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001415 if (loop == 2)
1416 fp->uf_flags |= FC_OBJECT;
1417 }
1418 }
1419
1420 return OK;
1421}
1422
1423/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001424 * Update the index of object methods called by builtin functions.
1425 */
1426 static void
1427update_builtin_method_index(class_T *cl)
1428{
1429 int i;
1430
1431 for (i = 0; i < CLASS_BUILTIN_MAX; i++)
1432 cl->class_builtin_methods[i] = -1;
1433
1434 for (i = 0; i < cl->class_obj_method_count; i++)
1435 {
1436 ufunc_T *uf = cl->class_obj_methods[i];
1437
1438 if (cl->class_builtin_methods[CLASS_BUILTIN_STRING] == -1
1439 && STRCMP(uf->uf_name, "string") == 0)
1440 cl->class_builtin_methods[CLASS_BUILTIN_STRING] = i;
1441 else if (cl->class_builtin_methods[CLASS_BUILTIN_EMPTY] == -1 &&
1442 STRCMP(uf->uf_name, "empty") == 0)
1443 cl->class_builtin_methods[CLASS_BUILTIN_EMPTY] = i;
1444 else if (cl->class_builtin_methods[CLASS_BUILTIN_LEN] == -1 &&
1445 STRCMP(uf->uf_name, "len") == 0)
1446 cl->class_builtin_methods[CLASS_BUILTIN_LEN] = i;
1447 }
1448}
1449
1450/*
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001451 * Return the end of the class name starting at "arg". Valid characters in a
1452 * class name are alphanumeric characters and "_". Also handles imported class
1453 * names.
1454 */
1455 static char_u *
1456find_class_name_end(char_u *arg)
1457{
1458 char_u *end = arg;
1459
1460 while (ASCII_ISALNUM(*end) || *end == '_'
1461 || (*end == '.' && (ASCII_ISALNUM(end[1]) || end[1] == '_')))
1462 ++end;
1463
1464 return end;
1465}
1466
1467
1468/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001469 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001470 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001471 */
1472 void
1473ex_class(exarg_T *eap)
1474{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001475 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1476 long start_lnum = SOURCING_LNUM;
1477 char_u *arg = eap->arg;
1478 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001479
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001480 if (is_abstract)
1481 {
1482 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1483 {
1484 semsg(_(e_invalid_argument_str), arg);
1485 return;
1486 }
1487 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001488 is_class = TRUE;
1489 }
1490
1491 if (!current_script_is_vim9()
1492 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001493 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001494 {
1495 if (is_class)
1496 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1497 else
1498 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1499 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001500 }
1501
1502 if (!ASCII_ISUPPER(*arg))
1503 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001504 if (is_class)
1505 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1506 else
1507 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1508 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001509 return;
1510 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001511 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1512 if (!IS_WHITE_OR_NUL(*name_end))
1513 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001514 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001515 return;
1516 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001517 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001518
Bram Moolenaara86655a2023-01-12 17:06:27 +00001519 // "export class" gets used when creating the class, don't use "is_export"
1520 // for the items inside the class.
1521 int class_export = is_export;
1522 is_export = FALSE;
1523
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001524 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001525 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001526
Bram Moolenaar83677162023-01-08 19:54:10 +00001527 // Name for "extends BaseClass"
1528 char_u *extends = NULL;
1529
Bram Moolenaar94674f22023-01-06 18:42:20 +00001530 // Names for "implements SomeInterface"
1531 garray_T ga_impl;
1532 ga_init2(&ga_impl, sizeof(char_u *), 5);
1533
1534 arg = skipwhite(name_end);
1535 while (*arg != NUL && *arg != '#' && *arg != '\n')
1536 {
1537 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001538 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001539 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1540 {
1541 if (extends != NULL)
1542 {
1543 emsg(_(e_duplicate_extends));
1544 goto early_ret;
1545 }
1546 arg = skipwhite(arg + 7);
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001547
1548 char_u *end = find_class_name_end(arg);
Bram Moolenaar83677162023-01-08 19:54:10 +00001549 if (!IS_WHITE_OR_NUL(*end))
1550 {
1551 semsg(_(e_white_space_required_after_name_str), arg);
1552 goto early_ret;
1553 }
1554 extends = vim_strnsave(arg, end - arg);
1555 if (extends == NULL)
1556 goto early_ret;
1557
1558 arg = skipwhite(end + 1);
1559 }
1560 else if (STRNCMP(arg, "implements", 10) == 0
1561 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001562 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001563 if (!is_class)
1564 {
1565 emsg(_(e_interface_cannot_use_implements));
1566 goto early_ret;
1567 }
1568
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001569 if (ga_impl.ga_len > 0)
1570 {
1571 emsg(_(e_duplicate_implements));
1572 goto early_ret;
1573 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001574 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001575
1576 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001577 {
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001578 char_u *impl_end = find_class_name_end(arg);
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001579 if ((!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1580 || (*impl_end == ','
1581 && !IS_WHITE_OR_NUL(*(impl_end + 1))))
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001582 {
1583 semsg(_(e_white_space_required_after_name_str), arg);
1584 goto early_ret;
1585 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001586 if (impl_end - arg == 0)
1587 {
1588 emsg(_(e_missing_name_after_implements));
1589 goto early_ret;
1590 }
1591
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001592 char_u *iname = vim_strnsave(arg, impl_end - arg);
1593 if (iname == NULL)
1594 goto early_ret;
1595 for (int i = 0; i < ga_impl.ga_len; ++i)
1596 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1597 {
1598 semsg(_(e_duplicate_interface_after_implements_str),
1599 iname);
1600 vim_free(iname);
1601 goto early_ret;
1602 }
1603 if (ga_add_string(&ga_impl, iname) == FAIL)
1604 {
1605 vim_free(iname);
1606 goto early_ret;
1607 }
1608 if (*impl_end != ',')
1609 {
1610 arg = skipwhite(impl_end);
1611 break;
1612 }
1613 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001614 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001615 }
1616 else
1617 {
1618 semsg(_(e_trailing_characters_str), arg);
1619early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001620 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001621 ga_clear_strings(&ga_impl);
1622 return;
1623 }
1624 }
1625
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001626 garray_T type_list; // list of pointers to allocated types
1627 ga_init2(&type_list, sizeof(type_T *), 10);
1628
Bram Moolenaard505d172022-12-18 21:42:55 +00001629 // Growarray with class members declared in the class.
1630 garray_T classmembers;
1631 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1632
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001633 // Growarray with functions declared in the class.
1634 garray_T classfunctions;
1635 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001636
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001637 // Growarray with object members declared in the class.
1638 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001639 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001640
1641 // Growarray with object methods declared in the class.
1642 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001643 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001644
1645 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001646 * Go over the body of the class/interface until "endclass" or
1647 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001648 */
1649 char_u *theline = NULL;
1650 int success = FALSE;
1651 for (;;)
1652 {
1653 vim_free(theline);
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001654 theline = eap->ea_getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001655 if (theline == NULL)
1656 break;
1657 char_u *line = skipwhite(theline);
1658
Bram Moolenaar418b5472022-12-20 13:38:22 +00001659 // Skip empty and comment lines.
1660 if (*line == NUL)
1661 continue;
1662 if (*line == '#')
1663 {
1664 if (vim9_bad_comment(line))
1665 break;
1666 continue;
1667 }
1668
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001669 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001670 char *end_name = is_class ? "endclass" : "endinterface";
1671 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001672 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001673 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001674 semsg(_(e_command_cannot_be_shortened_str), line);
1675 else if (*p == '|' || !ends_excmd2(line, p))
1676 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001677 else
1678 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001679 break;
1680 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001681 char *wrong_name = is_class ? "endinterface" : "endclass";
1682 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1683 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001684 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001685 break;
1686 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001687
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001688 int has_public = FALSE;
1689 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001690 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001691 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001692 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001693 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001694 break;
1695 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001696 if (!is_class)
1697 {
Yegappan Lakshmananb90e3bc2023-09-28 23:06:48 +02001698 emsg(_(e_public_variable_not_supported_in_interface));
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001699 break;
1700 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001701 has_public = TRUE;
1702 p = skipwhite(line + 6);
1703
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001704 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "static", 6) != 0
1705 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001706 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001707 emsg(_(e_public_must_be_followed_by_var_static_final_or_const));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001708 break;
1709 }
1710 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001711
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001712 int abstract_method = FALSE;
1713 char_u *pa = p;
1714 if (checkforcmd(&p, "abstract", 3))
1715 {
1716 if (STRNCMP(pa, "abstract", 8) != 0)
1717 {
1718 semsg(_(e_command_cannot_be_shortened_str), pa);
1719 break;
1720 }
1721
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01001722 if (!is_class)
1723 {
1724 // "abstract" not supported in an interface
1725 emsg(_(e_abstract_cannot_be_used_in_interface));
1726 break;
1727 }
1728
1729 if (!is_abstract)
1730 {
1731 semsg(_(e_abstract_method_in_concrete_class), pa);
1732 break;
1733 }
1734
Yegappan Lakshmanan5a539252023-11-04 09:42:46 +01001735 p = skipwhite(pa + 8);
1736 if (STRNCMP(p, "def", 3) != 0)
1737 {
1738 emsg(_(e_abstract_must_be_followed_by_def));
1739 break;
1740 }
1741
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01001742 abstract_method = TRUE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001743 }
1744
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001745 int has_static = FALSE;
1746 char_u *ps = p;
1747 if (checkforcmd(&p, "static", 4))
1748 {
1749 if (STRNCMP(ps, "static", 6) != 0)
1750 {
1751 semsg(_(e_command_cannot_be_shortened_str), ps);
1752 break;
1753 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001754
1755 if (!is_class)
1756 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001757 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001758 break;
1759 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001760 has_static = TRUE;
1761 p = skipwhite(ps + 6);
Doug Kearns74da0ee2023-12-14 20:26:26 +01001762
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001763 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "def", 3) != 0
1764 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Doug Kearns74da0ee2023-12-14 20:26:26 +01001765 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001766 emsg(_(e_static_must_be_followed_by_var_def_final_or_const));
Doug Kearns74da0ee2023-12-14 20:26:26 +01001767 break;
1768 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001769 }
1770
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001771 int has_final = FALSE;
1772 int has_var = FALSE;
1773 int has_const = FALSE;
1774 if (checkforcmd(&p, "var", 3))
1775 has_var = TRUE;
1776 else if (checkforcmd(&p, "final", 5))
1777 {
1778 if (!is_class)
1779 {
1780 emsg(_(e_final_variable_not_supported_in_interface));
1781 break;
1782 }
1783 has_final = TRUE;
1784 }
1785 else if (checkforcmd(&p, "const", 5))
1786 {
1787 if (!is_class)
1788 {
1789 emsg(_(e_const_variable_not_supported_in_interface));
1790 break;
1791 }
1792 has_const = TRUE;
1793 }
1794 p = skipwhite(p);
1795
Bram Moolenaard505d172022-12-18 21:42:55 +00001796 // object members (public, read access, private):
Doug Kearns74da0ee2023-12-14 20:26:26 +01001797 // "var _varname"
1798 // "var varname"
1799 // "public var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001800 // "final _varname"
1801 // "final varname"
1802 // "public final varname"
1803 // "const _varname"
1804 // "const varname"
1805 // "public const varname"
Doug Kearns74da0ee2023-12-14 20:26:26 +01001806 // class members (public, read access, private):
1807 // "static var _varname"
1808 // "static var varname"
1809 // "public static var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001810 // "static final _varname"
1811 // "static final varname"
1812 // "public static final varname"
1813 // "static const _varname"
1814 // "static const varname"
1815 // "public static const varname"
1816 if (has_var || has_final || has_const)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001817 {
Doug Kearns74da0ee2023-12-14 20:26:26 +01001818 char_u *varname = p;
Bram Moolenaard505d172022-12-18 21:42:55 +00001819 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001820 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001821 char_u *init_expr = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001822 int has_type = FALSE;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001823
Doug Kearns74da0ee2023-12-14 20:26:26 +01001824 if (!eval_isnamec1(*p))
1825 {
1826 if (has_static)
1827 semsg(_(e_invalid_class_variable_declaration_str), line);
1828 else
1829 semsg(_(e_invalid_object_variable_declaration_str), line);
1830 break;
1831 }
1832
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001833 if (!is_class && *varname == '_')
1834 {
1835 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01001836 semsg(_(e_protected_variable_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001837 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001838 break;
1839 }
1840
Bram Moolenaard505d172022-12-18 21:42:55 +00001841 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001842 &varname_end, &has_type, &type_list, &type,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001843 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001844 break;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001845
1846 if (is_reserved_varname(varname, varname_end)
1847 || is_duplicate_variable(&classmembers, &objmembers,
1848 varname, varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001849 {
1850 vim_free(init_expr);
1851 break;
1852 }
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001853 if (add_member(has_static ? &classmembers : &objmembers, varname,
1854 varname_end, has_public, has_final, has_const,
1855 has_type, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001856 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001857 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001858 break;
1859 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001860 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001861
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001862 // constructors:
1863 // def new()
1864 // enddef
1865 // def newOther()
1866 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001867 // object methods and class functions:
1868 // def SomeMethod()
1869 // enddef
1870 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001871 // enddef
1872 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001873 // def <Tval> someMethod()
1874 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001875 else if (checkforcmd(&p, "def", 3))
1876 {
1877 exarg_T ea;
1878 garray_T lines_to_free;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001879 int is_new = STRNCMP(p, "new", 3) == 0;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001880
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001881 if (has_public)
1882 {
1883 // "public" keyword is not supported when defining an object or
1884 // class method
1885 emsg(_(e_public_keyword_not_supported_for_method));
1886 break;
1887 }
1888
1889 if (*p == NUL)
1890 {
1891 // No method name following def
1892 semsg(_(e_not_valid_command_in_class_str), line);
1893 break;
1894 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001895
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001896 if (!is_class && *p == '_')
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01001897 {
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001898 // private methods are not supported in an interface
1899 semsg(_(e_protected_method_not_supported_in_interface), p);
1900 break;
1901 }
1902
1903 if (has_static && !is_new && SAFE_islower(*p) &&
1904 is_valid_builtin_obj_methodname(p))
1905 {
1906 semsg(_(e_builtin_class_method_not_supported), p);
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01001907 break;
1908 }
1909
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001910 CLEAR_FIELD(ea);
1911 ea.cmd = line;
1912 ea.arg = p;
1913 ea.cmdidx = CMD_def;
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001914 ea.ea_getline = eap->ea_getline;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001915 ea.cookie = eap->cookie;
1916
1917 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001918 int class_flags;
1919 if (is_class)
1920 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
1921 else
1922 class_flags = CF_INTERFACE;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001923 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02001924 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001925 ga_clear_strings(&lines_to_free);
1926
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001927 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001928 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001929 char_u *name = uf->uf_name;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001930
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001931 if (is_new && !is_valid_constructor(uf, is_abstract,
1932 has_static))
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001933 {
1934 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01001935 semsg(_(e_protected_method_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001936 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001937 func_clear_free(uf, FALSE);
1938 break;
1939 }
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001940
1941 // check for builtin method
1942 if (!is_new && SAFE_islower(*name) &&
1943 !object_check_builtin_method_sig(uf))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001944 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001945 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001946 break;
1947 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001948
Bram Moolenaar58b40092023-01-11 15:59:05 +00001949 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001950 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001951 {
1952 success = FALSE;
1953 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001954 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001955 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001956
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001957 garray_T *fgap = has_static || is_new
1958 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001959 if (ga_grow(fgap, 1) == OK)
1960 {
1961 if (is_new)
1962 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001963
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001964 if (abstract_method)
1965 uf->uf_flags |= FC_ABSTRACT;
1966
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001967 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1968 ++fgap->ga_len;
1969 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001970 }
1971 }
1972
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001973 else
1974 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001975 if (is_class)
1976 semsg(_(e_not_valid_command_in_class_str), line);
1977 else
1978 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001979 break;
1980 }
1981 }
1982 vim_free(theline);
1983
Bram Moolenaar83677162023-01-08 19:54:10 +00001984 class_T *extends_cl = NULL; // class from "extends" argument
1985
1986 /*
1987 * Check a few things before defining the class.
1988 */
1989
1990 // Check the "extends" class is valid.
1991 if (success && extends != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001992 success = validate_extends_class(extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00001993 VIM_CLEAR(extends);
1994
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001995 // Check the new object methods to make sure their access (public or
1996 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001997 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001998 success = validate_extends_methods(&objmethods, extends_cl);
1999
2000 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002001 // variables in the extended class lineage. If an interface is extending
2002 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002003 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002004 {
2005 if (is_class)
2006 success = extends_check_dup_members(&objmembers, extends_cl);
2007 else
2008 success = extends_check_intf_var_type(&objmembers, extends_cl);
2009 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002010
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002011 // When extending an abstract class, make sure all the abstract methods in
2012 // the parent class are implemented. If the current class is an abstract
2013 // class, then there is no need for this check.
2014 if (success && !is_abstract && extends_cl != NULL
2015 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002016 success = validate_abstract_class_methods(&classfunctions,
2017 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002018
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002019 class_T **intf_classes = NULL;
2020
Bram Moolenaar83677162023-01-08 19:54:10 +00002021 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00002022 if (success && ga_impl.ga_len > 0)
2023 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002024 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
2025
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002026 success = validate_implements_classes(&ga_impl, intf_classes,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002027 &objmethods, &objmembers, extends_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00002028 }
2029
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002030 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00002031 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002032 success = check_func_arg_names(&classfunctions, &objmethods,
2033 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00002034
Bram Moolenaareb533502022-12-14 15:06:11 +00002035 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002036 if (success)
2037 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002038 // "endclass" encountered without failures: Create the class.
2039
Bram Moolenaareb533502022-12-14 15:06:11 +00002040 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002041 if (cl == NULL)
2042 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00002043 if (!is_class)
2044 cl->class_flags = CLASS_INTERFACE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002045 else if (is_abstract)
2046 cl->class_flags = CLASS_ABSTRACT;
Bram Moolenaar554d0312023-01-05 19:59:18 +00002047
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002048 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00002049 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00002050 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002051 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00002052
Bram Moolenaard0200c82023-01-28 15:19:40 +00002053 if (extends_cl != NULL)
2054 {
2055 cl->class_extends = extends_cl;
2056 extends_cl->class_flags |= CLASS_EXTENDED;
2057 }
Bram Moolenaar83677162023-01-08 19:54:10 +00002058
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002059 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00002060 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002061 NULL,
2062 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00002063 &cl->class_class_members,
2064 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00002065 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00002066 extends_cl == NULL ? NULL
2067 : extends_cl->class_obj_members,
2068 extends_cl == NULL ? 0
2069 : extends_cl->class_obj_member_count,
2070 &cl->class_obj_members,
2071 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002072 goto cleanup;
2073
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00002074 if (ga_impl.ga_len > 0)
2075 {
2076 // Move the "implements" names into the class.
2077 cl->class_interface_count = ga_impl.ga_len;
2078 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
2079 if (cl->class_interfaces == NULL)
2080 goto cleanup;
2081 for (int i = 0; i < ga_impl.ga_len; ++i)
2082 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
2083 VIM_CLEAR(ga_impl.ga_data);
2084 ga_impl.ga_len = 0;
2085
Bram Moolenaard0200c82023-01-28 15:19:40 +00002086 cl->class_interfaces_cl = intf_classes;
2087 intf_classes = NULL;
2088 }
2089
2090 if (cl->class_interface_count > 0 || extends_cl != NULL)
2091 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002092 // Add a method and member lookup table to each of the interface
2093 // classes.
2094 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
2095 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00002096 }
2097
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002098 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00002099 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02002100 add_class_members(cl, eap, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002101
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002102 int have_new = FALSE;
2103 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002104 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002105 {
2106 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
2107 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002108 {
2109 have_new = TRUE;
2110 break;
2111 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002112 }
2113
2114 if (have_new)
2115 // The return type of new() is an object of class "cl"
2116 class_func->uf_ret_type->tt_class = cl;
2117 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002118 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002119 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002120
Bram Moolenaar58b40092023-01-11 15:59:05 +00002121 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002122 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
2123 &objmethods) == FAIL)
2124 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002125
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002126 update_builtin_method_index(cl);
2127
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002128 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00002129 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002130 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00002131 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002132 cl->class_type_list = type_list;
2133
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002134 class_created(cl);
2135
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002136 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00002137 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002138
2139 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00002140 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02002141 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002142 typval_T tv;
2143 tv.v_type = VAR_CLASS;
2144 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00002145 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00002146 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002147 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00002148 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002149 return;
2150 }
2151
2152cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00002153 if (cl != NULL)
2154 {
2155 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002156 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002157 if (cl->class_interfaces != NULL)
2158 {
2159 for (int i = 0; i < cl->class_interface_count; ++i)
2160 vim_free(cl->class_interfaces[i]);
2161 vim_free(cl->class_interfaces);
2162 }
2163 if (cl->class_interfaces_cl != NULL)
2164 {
2165 for (int i = 0; i < cl->class_interface_count; ++i)
2166 class_unref(cl->class_interfaces_cl[i]);
2167 vim_free(cl->class_interfaces_cl);
2168 }
Bram Moolenaareb533502022-12-14 15:06:11 +00002169 vim_free(cl->class_obj_members);
2170 vim_free(cl->class_obj_methods);
2171 vim_free(cl);
2172 }
2173
Bram Moolenaar83677162023-01-08 19:54:10 +00002174 vim_free(extends);
2175 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002176
2177 if (intf_classes != NULL)
2178 {
2179 for (int i = 0; i < ga_impl.ga_len; ++i)
2180 class_unref(intf_classes[i]);
2181 vim_free(intf_classes);
2182 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00002183 ga_clear_strings(&ga_impl);
2184
Bram Moolenaard505d172022-12-18 21:42:55 +00002185 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002186 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002187 garray_T *gap = round == 1 ? &classmembers : &objmembers;
2188 if (gap->ga_len == 0 || gap->ga_data == NULL)
2189 continue;
2190
2191 for (int i = 0; i < gap->ga_len; ++i)
2192 {
2193 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2194 vim_free(m->ocm_name);
2195 vim_free(m->ocm_init);
2196 }
2197 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002198 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002199
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002200 for (int i = 0; i < objmethods.ga_len; ++i)
2201 {
2202 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2203 func_clear_free(uf, FALSE);
2204 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002205 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002206
2207 for (int i = 0; i < classfunctions.ga_len; ++i)
2208 {
2209 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2210 func_clear_free(uf, FALSE);
2211 }
2212 ga_clear(&classfunctions);
2213
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002214 clear_type_list(&type_list);
2215}
2216
2217/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002218 * Find member "name" in class "cl", set "member_idx" to the member index and
2219 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002220 * When "is_object" is TRUE, then look for object members. Otherwise look for
2221 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002222 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002223 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002224 */
2225 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002226oc_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002227 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002228 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002229 char_u *name,
2230 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002231 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002232{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002233 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002234 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002235
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002236 *member_idx = -1; // not found (yet)
2237
2238 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2239 member_idx);
2240 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002241 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002242 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2243 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002244 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002245 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002246
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002247 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002248}
2249
2250/*
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002251 * Given a class or object variable index, return the variable type
2252 */
2253 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002254oc_member_type_by_idx(
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002255 class_T *cl,
2256 int is_object,
2257 int member_idx)
2258{
2259 ocmember_T *m;
2260 int member_count;
2261
2262 if (is_object)
2263 {
2264 m = cl->class_obj_members;
2265 member_count = cl->class_obj_member_count;
2266 }
2267 else
2268 {
2269 m = cl->class_class_members;
2270 member_count = cl->class_class_member_count;
2271 }
2272
2273 if (member_idx >= member_count)
2274 return NULL;
2275
2276 return m[member_idx].ocm_type;
2277}
2278
2279/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002280 * Handle ":enum" up to ":endenum".
2281 */
2282 void
2283ex_enum(exarg_T *eap UNUSED)
2284{
2285 // TODO
2286}
2287
2288/*
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002289 * Type aliases (:type)
2290 */
2291
2292 void
2293typealias_free(typealias_T *ta)
2294{
2295 // ta->ta_type is freed in clear_type_list()
2296 vim_free(ta->ta_name);
2297 vim_free(ta);
2298}
2299
2300 void
2301typealias_unref(typealias_T *ta)
2302{
2303 if (ta != NULL && --ta->ta_refcount <= 0)
2304 typealias_free(ta);
2305}
2306
2307/*
2308 * Handle ":type". Create an alias for a type specification.
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002309 */
2310 void
2311ex_type(exarg_T *eap UNUSED)
2312{
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002313 char_u *arg = eap->arg;
2314
2315 if (!current_script_is_vim9()
2316 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002317 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002318 {
2319 emsg(_(e_type_can_only_be_defined_in_vim9_script));
2320 return;
2321 }
2322
2323 if (*arg == NUL)
2324 {
2325 emsg(_(e_missing_typealias_name));
2326 return;
2327 }
2328
2329 if (!ASCII_ISUPPER(*arg))
2330 {
2331 semsg(_(e_type_name_must_start_with_uppercase_letter_str), arg);
2332 return;
2333 }
2334
2335 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
2336 if (!IS_WHITE_OR_NUL(*name_end))
2337 {
2338 semsg(_(e_white_space_required_after_name_str), arg);
2339 return;
2340 }
2341 char_u *name_start = arg;
2342
2343 arg = skipwhite(name_end);
2344 if (*arg != '=')
2345 {
2346 semsg(_(e_missing_equal_str), arg);
2347 return;
2348 }
2349 if (!IS_WHITE_OR_NUL(*(arg + 1)))
2350 {
2351 semsg(_(e_white_space_required_after_str_str), "=", arg);
2352 return;
2353 }
2354 arg++;
2355 arg = skipwhite(arg);
2356
2357 if (*arg == NUL)
2358 {
2359 emsg(_(e_missing_typealias_type));
2360 return;
2361 }
2362
2363 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
2364 type_T *type = parse_type(&arg, &si->sn_type_list, TRUE);
2365 if (type == NULL)
2366 return;
2367
2368 if (*arg != NUL)
2369 {
2370 // some text after the type
2371 semsg(_(e_trailing_characters_str), arg);
2372 return;
2373 }
2374
2375 int cc = *name_end;
2376 *name_end = NUL;
2377
2378 typval_T tv;
2379 tv.v_type = VAR_UNKNOWN;
2380 if (eval_variable_import(name_start, &tv) == OK)
2381 {
2382 if (tv.v_type == VAR_TYPEALIAS)
2383 semsg(_(e_typealias_already_exists_for_str), name_start);
2384 else
2385 semsg(_(e_redefining_script_item_str), name_start);
2386 clear_tv(&tv);
2387 goto done;
2388 }
2389
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02002390 // Create a script-local variable for the type alias.
2391 if (type->tt_type != VAR_OBJECT)
2392 {
2393 tv.v_type = VAR_TYPEALIAS;
2394 tv.v_lock = 0;
2395 tv.vval.v_typealias = ALLOC_CLEAR_ONE(typealias_T);
2396 ++tv.vval.v_typealias->ta_refcount;
2397 tv.vval.v_typealias->ta_name = vim_strsave(name_start);
2398 tv.vval.v_typealias->ta_type = type;
2399 }
2400 else
2401 {
2402 // When creating a type alias for a class, use the class type itself to
2403 // create the type alias variable. This is needed to use the type
2404 // alias to invoke class methods (e.g. new()) and use class variables.
2405 tv.v_type = VAR_CLASS;
2406 tv.v_lock = 0;
2407 tv.vval.v_class = type->tt_class;
2408 ++tv.vval.v_class->class_refcount;
2409 }
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002410 set_var_const(name_start, current_sctx.sc_sid, NULL, &tv, FALSE,
2411 ASSIGN_CONST | ASSIGN_FINAL, 0);
2412
2413done:
2414 *name_end = cc;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002415}
2416
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002417/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002418 * Returns OK if a member variable named "name" is present in the class "cl".
2419 * Otherwise returns FAIL. If found, the member variable typval is set in
2420 * "rettv". If "is_object" is TRUE, then the object member variable table is
2421 * searched. Otherwise the class member variable table is searched.
2422 */
2423 static int
2424get_member_tv(
2425 class_T *cl,
2426 int is_object,
2427 char_u *name,
2428 size_t namelen,
2429 typval_T *rettv)
2430{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002431 ocmember_T *m;
2432 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002433
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002434 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2435 &m_idx);
2436 if (m == NULL)
2437 return FAIL;
2438
2439 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002440 {
Ernie Rael03042a22023-11-11 08:53:32 +01002441 emsg_var_cl_define(e_cannot_access_protected_variable_str,
Ernie Raele6c9aa52023-10-06 19:55:52 +02002442 m->ocm_name, 0, cl);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002443 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002444 }
2445
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002446 if (is_object)
2447 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002448 // The object only contains a pointer to the class, the member values
2449 // array follows right after that.
2450 object_T *obj = rettv->vval.v_object;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002451 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2452 copy_tv(tv, rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002453 object_unref(obj);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002454 }
2455 else
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002456 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002457 copy_tv(&cl->class_members_tv[m_idx], rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002458 class_unref(cl);
2459 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002460
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002461 return OK;
2462}
2463
2464/*
2465 * Call an object or class method "name" in class "cl". The method return
2466 * value is returned in "rettv".
2467 */
2468 static int
2469call_oc_method(
2470 class_T *cl,
2471 char_u *name,
2472 size_t len,
2473 char_u *name_end,
2474 evalarg_T *evalarg,
2475 char_u **arg,
2476 typval_T *rettv)
2477{
2478 ufunc_T *fp;
2479 typval_T argvars[MAX_FUNC_ARGS + 1];
2480 int argcount = 0;
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002481 ocmember_T *ocm = NULL;
2482 int m_idx;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002483
2484 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
2485 if (fp == NULL)
2486 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002487 // could be an object or class funcref variable
2488 ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
2489 if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
2490 {
2491 method_not_found_msg(cl, rettv->v_type, name, len);
2492 return FAIL;
2493 }
2494
2495 if (rettv->v_type == VAR_OBJECT)
2496 {
2497 // funcref object variable
2498 object_T *obj = rettv->vval.v_object;
2499 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2500 copy_tv(tv, rettv);
2501 }
2502 else
2503 // funcref class variable
2504 copy_tv(&cl->class_members_tv[m_idx], rettv);
2505 *arg = name_end;
2506 return OK;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002507 }
2508
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002509 if (ocm == NULL && *fp->uf_name == '_')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002510 {
2511 // Cannot access a private method outside of a class
Ernie Rael03042a22023-11-11 08:53:32 +01002512 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002513 return FAIL;
2514 }
2515
2516 char_u *argp = name_end;
Ernie Raelb077b582023-12-14 20:11:44 +01002517 int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount, FALSE);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002518 if (ret == FAIL)
2519 return FAIL;
2520
2521 funcexe_T funcexe;
2522 CLEAR_FIELD(funcexe);
2523 funcexe.fe_evaluate = TRUE;
2524 if (rettv->v_type == VAR_OBJECT)
2525 {
2526 funcexe.fe_object = rettv->vval.v_object;
2527 ++funcexe.fe_object->obj_refcount;
2528 }
2529
2530 // Clear the class or object after calling the function, in
2531 // case the refcount is one.
2532 typval_T tv_tofree = *rettv;
2533 rettv->v_type = VAR_UNKNOWN;
2534
2535 // Call the user function. Result goes into rettv;
2536 int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
2537 NULL);
2538
2539 // Clear the previous rettv and the arguments.
2540 clear_tv(&tv_tofree);
2541 for (int idx = 0; idx < argcount; ++idx)
2542 clear_tv(&argvars[idx]);
2543
2544 if (error != FCERR_NONE)
2545 {
2546 user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
2547 return FAIL;
2548 }
2549 *arg = argp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002550
2551 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002552}
2553
2554/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002555 * Evaluate what comes after a class:
2556 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002557 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002558 * - class constructor: SomeClass.new()
2559 * - object member: someObject.varname
2560 * - object method: someObject.SomeMethod()
2561 *
2562 * "*arg" points to the '.'.
2563 * "*arg" is advanced to after the member name or method call.
2564 *
2565 * Returns FAIL or OK.
2566 */
2567 int
2568class_object_index(
2569 char_u **arg,
2570 typval_T *rettv,
2571 evalarg_T *evalarg,
2572 int verbose UNUSED) // give error messages
2573{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002574 if (VIM_ISWHITE((*arg)[1]))
2575 {
2576 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
2577 return FAIL;
2578 }
2579
2580 ++*arg;
2581 char_u *name = *arg;
2582 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2583 if (name_end == name)
2584 return FAIL;
2585 size_t len = name_end - name;
2586
Ernie Raeld615a312023-10-05 20:28:16 +02002587 int did_emsg_save = did_emsg;
Bram Moolenaar552bdca2023-02-17 21:08:50 +00002588 class_T *cl;
2589 if (rettv->v_type == VAR_CLASS)
2590 cl = rettv->vval.v_class;
2591 else // VAR_OBJECT
2592 {
2593 if (rettv->vval.v_object == NULL)
2594 {
2595 emsg(_(e_using_null_object));
2596 return FAIL;
2597 }
2598 cl = rettv->vval.v_object->obj_class;
2599 }
2600
Bram Moolenaard13dd302023-03-11 20:56:35 +00002601 if (cl == NULL)
2602 {
2603 emsg(_(e_incomplete_type));
2604 return FAIL;
2605 }
2606
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002607 if (*name_end == '(')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002608 // Invoke the class or object method
2609 return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002610
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002611 else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002612 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002613 // Search in the object member variable table and the class member
2614 // variable table.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002615 int is_object = rettv->v_type == VAR_OBJECT;
2616 if (get_member_tv(cl, is_object, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002617 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002618 *arg = name_end;
2619 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002620 }
2621
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002622 // could be a class method or an object method
2623 int fidx;
2624 ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
2625 if (fp != NULL)
2626 {
2627 // Private methods are not accessible outside the class
2628 if (*name == '_')
2629 {
Ernie Rael03042a22023-11-11 08:53:32 +01002630 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002631 return FAIL;
2632 }
2633
2634 partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
2635 if (pt == NULL)
2636 return FAIL;
2637
2638 pt->pt_refcount = 1;
2639 if (is_object)
2640 {
2641 pt->pt_obj = rettv->vval.v_object;
2642 ++pt->pt_obj->obj_refcount;
2643 }
2644 pt->pt_auto = TRUE;
2645 pt->pt_func = fp;
2646 func_ptr_ref(pt->pt_func);
2647 rettv->v_type = VAR_PARTIAL;
2648 rettv->vval.v_partial = pt;
2649 *arg = name_end;
2650 return OK;
2651 }
2652
Ernie Raeld615a312023-10-05 20:28:16 +02002653 if (did_emsg == did_emsg_save)
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +02002654 member_not_found_msg(cl, rettv->v_type, name, len);
Bram Moolenaard505d172022-12-18 21:42:55 +00002655 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002656
2657 return FAIL;
2658}
2659
2660/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002661 * If "arg" points to a class or object method, return it.
2662 * Otherwise return NULL.
2663 */
2664 ufunc_T *
2665find_class_func(char_u **arg)
2666{
2667 char_u *name = *arg;
2668 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2669 if (name_end == name || *name_end != '.')
2670 return NULL;
2671
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002672 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002673 size_t len = name_end - name;
2674 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002675 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00002676 if (eval_variable(name, (int)len,
2677 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002678 return NULL;
2679 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00002680 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002681
2682 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
2683 : tv.vval.v_object->obj_class;
2684 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002685 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002686 char_u *fname = name_end + 1;
2687 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
2688 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00002689 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002690 len = fname_end - fname;
2691
Ernie Rael4d00b832023-09-11 19:54:42 +02002692 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002693
Bram Moolenaareb533502022-12-14 15:06:11 +00002694fail_after_eval:
2695 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002696 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002697}
2698
2699/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002700 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002701 * Returns -1, if the variable is not found.
2702 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002703 */
2704 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002705class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002706{
Ernie Rael4d00b832023-09-11 19:54:42 +02002707 int idx;
2708 class_member_lookup(cl, name, namelen, &idx);
2709 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002710}
2711
2712/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002713 * Returns a pointer to the class member variable "name" in the class "cl".
2714 * Returns NULL if the variable is not found.
2715 * The member variable index is set in "idx".
2716 */
2717 ocmember_T *
2718class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2719{
Ernie Rael4d00b832023-09-11 19:54:42 +02002720 ocmember_T *ret_m = NULL;
2721 int ret_idx = -1;
2722 for (int i = 0; i < cl->class_class_member_count; ++i)
2723 {
2724 ocmember_T *m = &cl->class_class_members[i];
2725 if (namelen)
2726 {
2727 if (STRNCMP(name, m->ocm_name, namelen) == 0
2728 && m->ocm_name[namelen] == NUL)
2729 {
2730 ret_m = m;
2731 ret_idx = i;
2732 break;
2733 }
2734 }
2735 else if (STRCMP(name, m->ocm_name) == 0)
2736 {
2737 ret_m = m;
2738 ret_idx = i;
2739 break;
2740 }
2741 }
2742 if (idx != NULL)
2743 *idx = ret_idx;
2744 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002745}
2746
2747/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002748 * Returns a pointer to the class method "name" in class "cl".
2749 * Returns NULL if the method is not found.
2750 * The method index is set in "idx".
2751 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002752 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002753class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2754{
Ernie Rael4d00b832023-09-11 19:54:42 +02002755 ufunc_T *ret_fp = NULL;
2756 int ret_idx = -1;
2757 for (int i = 0; i < cl->class_class_function_count; ++i)
2758 {
2759 ufunc_T *fp = cl->class_class_functions[i];
2760 char_u *ufname = (char_u *)fp->uf_name;
2761 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2762 {
2763 ret_fp = fp;
2764 ret_idx = i;
2765 break;
2766 }
2767 }
2768 if (idx != NULL)
2769 *idx = ret_idx;
2770 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002771}
2772
2773/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002774 * Returns the index of class method "name" in the class "cl".
2775 * Returns -1, if the method is not found.
2776 */
2777 int
2778class_method_idx(class_T *cl, char_u *name, size_t namelen)
2779{
2780 int idx;
2781 class_method_lookup(cl, name, namelen, &idx);
2782 return idx;
2783}
2784
2785/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002786 * Returns the index of object member variable "name" in the class "cl".
2787 * Returns -1, if the variable is not found.
2788 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
2789 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002790 static int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002791object_member_idx(class_T *cl, char_u *name, size_t namelen)
2792{
Ernie Rael4d00b832023-09-11 19:54:42 +02002793 int idx;
2794 object_member_lookup(cl, name, namelen, &idx);
2795 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002796}
2797
2798/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002799 * Returns a pointer to the object member variable "name" in the class "cl".
2800 * Returns NULL if the variable is not found.
2801 * The object member variable index is set in "idx".
2802 */
2803 ocmember_T *
2804object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2805{
Ernie Rael4d00b832023-09-11 19:54:42 +02002806 ocmember_T *ret_m = NULL;
2807 int ret_idx = -1;
2808 for (int i = 0; i < cl->class_obj_member_count; ++i)
2809 {
2810 ocmember_T *m = &cl->class_obj_members[i];
2811 if (namelen)
2812 {
2813 if (STRNCMP(name, m->ocm_name, namelen) == 0
2814 && m->ocm_name[namelen] == NUL)
2815 {
2816 ret_m = m;
2817 ret_idx = i;
2818 break;
2819 }
2820 }
2821 else if (STRCMP(name, m->ocm_name) == 0)
2822 {
2823 ret_m = m;
2824 ret_idx = i;
2825 break;
2826 }
2827 }
2828 if (idx != NULL)
2829 *idx = ret_idx;
2830 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002831}
2832
2833/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002834 * Returns a pointer to the object method "name" in class "cl".
2835 * Returns NULL if the method is not found.
2836 * The object method index is set in "idx".
2837 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002838 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002839object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2840{
Ernie Rael4d00b832023-09-11 19:54:42 +02002841 ufunc_T *ret_fp = NULL;
2842 int ret_idx = -1;
2843 for (int i = 0; i < cl->class_obj_method_count; ++i)
2844 {
2845 ufunc_T *fp = cl->class_obj_methods[i];
2846 // Use a separate pointer to avoid that ASAN complains about
2847 // uf_name[] only being 4 characters.
2848 char_u *ufname = (char_u *)fp->uf_name;
2849 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2850 {
2851 ret_fp = fp;
2852 ret_idx = i;
2853 break;
2854 }
2855 }
2856 if (idx != NULL)
2857 *idx = ret_idx;
2858 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002859}
2860
2861/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002862 * Returns the index of object method "name" in the class "cl".
2863 * Returns -1, if the method is not found.
2864 */
2865 int
2866object_method_idx(class_T *cl, char_u *name, size_t namelen)
2867{
2868 int idx;
2869 object_method_lookup(cl, name, namelen, &idx);
2870 return idx;
2871}
2872
2873/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002874 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
2875 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
2876 * object member variable.
2877 *
2878 * Returns a pointer to the member variable structure if variable is found.
2879 * Otherwise returns NULL. The member variable index is set in "*idx".
2880 */
2881 ocmember_T *
2882member_lookup(
2883 class_T *cl,
2884 vartype_T v_type,
2885 char_u *name,
2886 size_t namelen,
2887 int *idx)
2888{
2889 if (v_type == VAR_CLASS)
2890 return class_member_lookup(cl, name, namelen, idx);
2891 else
2892 return object_member_lookup(cl, name, namelen, idx);
2893}
2894
2895/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02002896 * Find the class that defines the named member. Look up the hierarchy
2897 * starting at "cl".
2898 *
2899 * Return the class that defines the member "name", else NULL.
2900 * Fill in "p_m", if specified, for ocmember_T in found class.
2901 */
2902// NOTE: if useful for something could also indirectly return vartype and idx.
2903 static class_T *
2904class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m)
2905{
2906 class_T *cl_found = NULL;
2907 vartype_T vartype = VAR_UNKNOWN;
2908 ocmember_T *m_found = NULL;
2909
2910 len = len != 0 ? len : STRLEN(name);
2911
2912 // Loop assumes if member is not defined in "cl", then it is not
2913 // defined in any super class; the last class where it's found is the
2914 // class where it is defined. Once the vartype is found, the other
2915 // type is no longer checked.
2916 for (class_T *super = cl; super != NULL; super = super->class_extends)
2917 {
2918 class_T *cl_tmp = NULL;
2919 ocmember_T *m = NULL;
2920 if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT)
2921 {
2922 if ((m = object_member_lookup(super, name, len, NULL)) != NULL)
2923 {
2924 cl_tmp = super;
2925 vartype = VAR_OBJECT;
2926 }
2927 }
2928 if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS)
2929 {
2930 if (( m = class_member_lookup(super, name, len, NULL)) != NULL)
2931 {
2932 cl_tmp = super;
2933 vartype = VAR_OBJECT;
2934 }
2935 }
2936 if (cl_tmp == NULL)
2937 break; // member is not in this or any super class.
2938 cl_found = cl_tmp;
2939 m_found = m;
2940 }
2941 if (p_m != NULL)
2942 *p_m = m_found;
2943 return cl_found;
2944}
2945
2946/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002947 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
2948 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
2949 *
2950 * Returns a pointer to the method structure if variable is found.
2951 * Otherwise returns NULL. The method variable index is set in "*idx".
2952 */
2953 ufunc_T *
2954method_lookup(
2955 class_T *cl,
2956 vartype_T v_type,
2957 char_u *name,
2958 size_t namelen,
2959 int *idx)
2960{
2961 if (v_type == VAR_CLASS)
2962 return class_method_lookup(cl, name, namelen, idx);
2963 else
2964 return object_method_lookup(cl, name, namelen, idx);
2965}
2966
2967/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00002968 * Return TRUE if current context "cctx_arg" is inside class "cl".
2969 * Return FALSE if not.
2970 */
2971 int
2972inside_class(cctx_T *cctx_arg, class_T *cl)
2973{
2974 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002975 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002976 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002977 return TRUE;
2978 return FALSE;
2979}
2980
2981/*
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002982 * Return TRUE if object/class variable "m" is read-only.
2983 * Also give an error message.
2984 */
2985 int
2986oc_var_check_ro(class_T *cl, ocmember_T *m)
2987{
2988 if (m->ocm_flags & (OCMFLAG_FINAL | OCMFLAG_CONST))
2989 {
2990 semsg(_(e_cannot_change_readonly_variable_str_in_class_str),
2991 m->ocm_name, cl->class_name);
2992 return TRUE;
2993 }
2994 return FALSE;
2995}
2996
2997/*
2998 * Lock all the constant object variables. Called after creating and
2999 * initializing a new object.
3000 */
3001 void
3002obj_lock_const_vars(object_T *obj)
3003{
3004 for (int i = 0; i < obj->obj_class->class_obj_member_count; i++)
3005 {
3006 ocmember_T *ocm = &obj->obj_class->class_obj_members[i];
3007 if (ocm->ocm_flags & OCMFLAG_CONST)
3008 {
3009 typval_T *mtv = ((typval_T *)(obj + 1)) + i;
3010 item_lock(mtv, DICT_MAXNEST, TRUE, TRUE);
3011 }
3012 }
3013}
3014
3015/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003016 * Make a copy of an object.
3017 */
3018 void
3019copy_object(typval_T *from, typval_T *to)
3020{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003021 if (from->vval.v_object == NULL)
3022 to->vval.v_object = NULL;
3023 else
3024 {
3025 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003026 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003027 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003028}
3029
3030/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003031 * Make a copy of a class.
3032 */
3033 void
3034copy_class(typval_T *from, typval_T *to)
3035{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003036 if (from->vval.v_class == NULL)
3037 to->vval.v_class = NULL;
3038 else
3039 {
3040 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003041 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003042 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003043}
3044
3045/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003046 * Free the class "cl" and its contents.
3047 */
3048 static void
3049class_free(class_T *cl)
3050{
3051 // Freeing what the class contains may recursively come back here.
3052 // Clear "class_name" first, if it is NULL the class does not need to
3053 // be freed.
3054 VIM_CLEAR(cl->class_name);
3055
3056 class_unref(cl->class_extends);
3057
3058 for (int i = 0; i < cl->class_interface_count; ++i)
3059 {
3060 vim_free(((char_u **)cl->class_interfaces)[i]);
3061 if (cl->class_interfaces_cl[i] != NULL)
3062 class_unref(cl->class_interfaces_cl[i]);
3063 }
3064 vim_free(cl->class_interfaces);
3065 vim_free(cl->class_interfaces_cl);
3066
3067 itf2class_T *next;
3068 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
3069 {
3070 next = i2c->i2c_next;
3071 vim_free(i2c);
3072 }
3073
3074 for (int i = 0; i < cl->class_class_member_count; ++i)
3075 {
3076 ocmember_T *m = &cl->class_class_members[i];
3077 vim_free(m->ocm_name);
3078 vim_free(m->ocm_init);
3079 if (cl->class_members_tv != NULL)
3080 clear_tv(&cl->class_members_tv[i]);
3081 }
3082 vim_free(cl->class_class_members);
3083 vim_free(cl->class_members_tv);
3084
3085 for (int i = 0; i < cl->class_obj_member_count; ++i)
3086 {
3087 ocmember_T *m = &cl->class_obj_members[i];
3088 vim_free(m->ocm_name);
3089 vim_free(m->ocm_init);
3090 }
3091 vim_free(cl->class_obj_members);
3092
3093 for (int i = 0; i < cl->class_class_function_count; ++i)
3094 {
3095 ufunc_T *uf = cl->class_class_functions[i];
3096 func_clear_free(uf, FALSE);
3097 }
3098 vim_free(cl->class_class_functions);
3099
3100 for (int i = 0; i < cl->class_obj_method_count; ++i)
3101 {
3102 ufunc_T *uf = cl->class_obj_methods[i];
3103 func_clear_free(uf, FALSE);
3104 }
3105 vim_free(cl->class_obj_methods);
3106
3107 clear_type_list(&cl->class_type_list);
3108
3109 class_cleared(cl);
3110
3111 vim_free(cl);
3112}
3113
3114/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003115 * Unreference a class. Free it when the reference count goes down to zero.
3116 */
3117 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003118class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003119{
Bram Moolenaard505d172022-12-18 21:42:55 +00003120 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003121 class_free(cl);
3122}
3123
3124/*
3125 * Go through the list of all classes and free items without "copyID".
3126 */
3127 int
3128class_free_nonref(int copyID)
3129{
3130 int did_free = FALSE;
3131
3132 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003133 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003134 next_nonref_class = cl->class_next_used;
3135 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003136 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003137 // Free the class and items it contains.
3138 class_free(cl);
3139 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003140 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003141 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003142
3143 next_nonref_class = NULL;
3144 return did_free;
3145}
3146
3147 int
3148set_ref_in_classes(int copyID)
3149{
3150 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3151 set_ref_in_item_class(cl, copyID, NULL, NULL);
3152
3153 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003154}
3155
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003156static object_T *first_object = NULL;
3157
3158/*
3159 * Call this function when an object has been created. It will be added to the
3160 * list headed by "first_object".
3161 */
3162 void
3163object_created(object_T *obj)
3164{
3165 if (first_object != NULL)
3166 {
3167 obj->obj_next_used = first_object;
3168 first_object->obj_prev_used = obj;
3169 }
3170 first_object = obj;
3171}
3172
3173/*
3174 * Call this function when an object has been cleared and is about to be freed.
3175 * It is removed from the list headed by "first_object".
3176 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003177 static void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003178object_cleared(object_T *obj)
3179{
3180 if (obj->obj_next_used != NULL)
3181 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
3182 if (obj->obj_prev_used != NULL)
3183 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
3184 else if (first_object == obj)
3185 first_object = obj->obj_next_used;
3186}
3187
3188/*
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003189 * Free the contents of an object ignoring the reference count.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003190 */
3191 static void
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003192object_free_contents(object_T *obj)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003193{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003194 class_T *cl = obj->obj_class;
3195
3196 if (!cl)
3197 return;
3198
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003199 // Avoid a recursive call, it can happen if "obj" has a circular reference.
3200 obj->obj_refcount = INT_MAX;
3201
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003202 // the member values are just after the object structure
3203 typval_T *tv = (typval_T *)(obj + 1);
3204 for (int i = 0; i < cl->class_obj_member_count; ++i)
3205 clear_tv(tv + i);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003206}
3207
3208 static void
3209object_free_object(object_T *obj)
3210{
3211 class_T *cl = obj->obj_class;
3212
3213 if (!cl)
3214 return;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003215
3216 // Remove from the list headed by "first_object".
3217 object_cleared(obj);
3218
3219 vim_free(obj);
3220 class_unref(cl);
3221}
3222
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003223 static void
3224object_free(object_T *obj)
3225{
3226 if (in_free_unref_items)
3227 return;
3228
3229 object_free_contents(obj);
3230 object_free_object(obj);
3231}
3232
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003233/*
3234 * Unreference an object.
3235 */
3236 void
3237object_unref(object_T *obj)
3238{
3239 if (obj != NULL && --obj->obj_refcount <= 0)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003240 object_free(obj);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003241}
3242
3243/*
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003244 * Go through the list of all objects and free items without "copyID".
3245 */
3246 int
3247object_free_nonref(int copyID)
3248{
3249 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003250
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003251 for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003252 {
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003253 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3254 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003255 // Free the object contents. Object itself will be freed later.
3256 object_free_contents(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003257 did_free = TRUE;
3258 }
3259 }
3260
3261 return did_free;
3262}
3263
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003264 void
3265object_free_items(int copyID)
3266{
3267 object_T *obj_next;
3268
3269 for (object_T *obj = first_object; obj != NULL; obj = obj_next)
3270 {
3271 obj_next = obj->obj_next_used;
3272 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3273 object_free_object(obj);
3274 }
3275}
3276
LemonBoyafe04662023-08-23 21:08:11 +02003277/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003278 * Output message which takes a variable name and the class that defines it.
3279 * "cl" is that class where the name was found. Search "cl"'s hierarchy to
3280 * find the defining class.
3281 */
3282 void
3283emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl)
3284{
3285 ocmember_T *m;
3286 class_T *cl_def = class_defining_member(cl, name, len, &m);
3287 if (cl_def != NULL)
3288 semsg(_(msg), m->ocm_name, cl_def->class_name);
3289 else
3290 emsg(_(e_internal_error_please_report_a_bug));
3291}
3292
3293/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003294 * Echo a class or object method not found message.
3295 */
3296 void
3297method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3298{
3299 char_u *method_name = vim_strnsave(name, len);
3300 if ((v_type == VAR_OBJECT)
3301 && (class_method_idx(cl, name, len) >= 0))
3302 {
3303 // If this is a class method, then give a different error
3304 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003305 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003306 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003307 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003308 method_name, cl->class_name);
3309 }
3310 else if ((v_type == VAR_CLASS)
3311 && (object_method_idx(cl, name, len) >= 0))
3312 {
3313 // If this is an object method, then give a different error
3314 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003315 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003316 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003317 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003318 method_name, cl->class_name);
3319 }
3320 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003321 semsg(_(e_method_not_found_on_class_str_str), method_name,
3322 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003323 vim_free(method_name);
3324}
3325
3326/*
3327 * Echo a class or object member not found message.
3328 */
3329 void
3330member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3331{
3332 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
3333
3334 if (v_type == VAR_OBJECT)
3335 {
3336 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003337 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003338 varname, cl->class_name);
3339 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003340 semsg(_(e_variable_not_found_on_object_str_str), varname,
3341 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003342 }
3343 else
3344 {
3345 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003346 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003347 varname, cl->class_name);
3348 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003349 semsg(_(e_class_variable_str_not_found_in_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003350 varname, cl->class_name);
3351 }
3352 vim_free(varname);
3353}
3354
3355/*
Yegappan Lakshmanan4f32c832024-01-12 17:36:40 +01003356 * Compile all the class and object methods in "cl".
3357 */
3358 void
3359defcompile_class(class_T *cl)
3360{
3361 for (int loop = 1; loop <= 2; ++loop)
3362 {
3363 int func_count = loop == 1 ? cl->class_class_function_count
3364 : cl->class_obj_method_count;
3365 for (int i = 0; i < func_count; i++)
3366 {
3367 ufunc_T *ufunc = loop == 1 ? cl->class_class_functions[i]
3368 : cl->class_obj_methods[i];
3369 defcompile_function(ufunc, cl);
3370 }
3371 }
3372}
3373
3374/*
3375 * Compile all the classes defined in the current script
3376 */
3377 void
3378defcompile_classes_in_script(void)
3379{
3380 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3381 {
3382 if (eval_variable(cl->class_name, 0, 0, NULL, NULL,
3383 EVAL_VAR_NOAUTOLOAD | EVAL_VAR_NO_FUNC) != FAIL)
3384 defcompile_class(cl);
3385 }
3386}
3387
3388/*
3389 * Returns TRUE if "name" is the name of a class. The typval for the class is
3390 * returned in "rettv".
3391 */
3392 int
3393is_class_name(char_u *name, typval_T *rettv)
3394{
3395 rettv->v_type = VAR_UNKNOWN;
3396
3397 if (eval_variable(name, 0, 0, rettv, NULL, EVAL_VAR_NOAUTOLOAD |
3398 EVAL_VAR_NO_FUNC) != FAIL)
3399 return rettv->v_type == VAR_CLASS;
3400 return FALSE;
3401}
3402
3403/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003404 * Calls the object builtin method "name" with arguments "argv". The value
3405 * returned by the builtin method is in "rettv". Returns OK or FAIL.
3406 */
3407 static int
3408object_call_builtin_method(
3409 object_T *obj,
3410 class_builtin_T builtin_method,
3411 int argc,
3412 typval_T *argv,
3413 typval_T *rettv)
3414{
3415 ufunc_T *uf;
3416 int midx;
3417
3418 if (obj == NULL)
3419 return FAIL;
3420
3421 uf = class_get_builtin_method(obj->obj_class, builtin_method, &midx);
3422 if (uf == NULL)
3423 return FAIL;
3424
3425 funccall_T *fc = create_funccal(uf, rettv);
3426 int r;
3427
3428 if (fc == NULL)
3429 return FAIL;
3430
3431 ++obj->obj_refcount;
3432
3433 r = call_def_function(uf, argc, argv, 0, NULL, obj, fc, rettv);
3434
3435 remove_funccal();
3436
3437 return r;
3438}
3439
3440/*
3441 * Calls the object "empty()" method and returns the method retun value. In
3442 * case of an error, returns TRUE.
3443 */
3444 int
3445object_empty(object_T *obj)
3446{
3447 typval_T rettv;
3448
3449 if (object_call_builtin_method(obj, CLASS_BUILTIN_EMPTY, 0, NULL, &rettv)
3450 == FAIL)
3451 return TRUE;
3452
3453 return tv_get_bool(&rettv);
3454}
3455
3456/*
3457 * Use the object "len()" method to get an object length. Returns 0 if the
3458 * method is not found or there is an error.
3459 */
3460 int
3461object_len(object_T *obj)
3462{
3463 typval_T rettv;
3464
3465 if (object_call_builtin_method(obj, CLASS_BUILTIN_LEN, 0, NULL, &rettv)
3466 == FAIL)
3467 return 0;
3468
3469 return tv_to_number(&rettv);
3470}
3471
3472/*
3473 * Return a textual representation of object "obj"
3474 */
3475 char_u *
3476object_string(
3477 object_T *obj,
3478 char_u *numbuf,
3479 int copyID,
3480 int echo_style,
3481 int restore_copyID,
3482 int composite_val)
3483{
3484 typval_T rettv;
3485
3486 if (object_call_builtin_method(obj, CLASS_BUILTIN_STRING, 0, NULL, &rettv)
3487 == OK
3488 && rettv.vval.v_string != NULL)
3489 return rettv.vval.v_string;
3490 else
3491 {
3492 garray_T ga;
3493 ga_init2(&ga, 1, 50);
3494
3495 ga_concat(&ga, (char_u *)"object of ");
3496 class_T *cl = obj == NULL ? NULL : obj->obj_class;
3497 ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]"
3498 : cl->class_name);
3499 if (cl != NULL)
3500 {
3501 ga_concat(&ga, (char_u *)" {");
3502 for (int i = 0; i < cl->class_obj_member_count; ++i)
3503 {
3504 if (i > 0)
3505 ga_concat(&ga, (char_u *)", ");
3506 ocmember_T *m = &cl->class_obj_members[i];
3507 ga_concat(&ga, m->ocm_name);
3508 ga_concat(&ga, (char_u *)": ");
3509 char_u *tf = NULL;
3510 ga_concat(&ga, echo_string_core(
3511 (typval_T *)(obj + 1) + i,
3512 &tf, numbuf, copyID, echo_style,
3513 restore_copyID, composite_val));
3514 vim_free(tf);
3515 }
3516 ga_concat(&ga, (char_u *)"}");
3517 }
3518 return ga.ga_data;
3519 }
3520}
3521
3522/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02003523 * Return TRUE when the class "cl", its base class or one of the implemented
3524 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02003525 */
3526 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003527class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02003528{
3529 if (cl == other_cl)
3530 return TRUE;
3531
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003532 // Recursively check the base classes.
3533 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02003534 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003535 if (cl == other_cl)
3536 return TRUE;
3537 // Check the implemented interfaces and the super interfaces
3538 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003539 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003540 class_T *intf = cl->class_interfaces_cl[i];
3541 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003542 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003543 if (intf == other_cl)
3544 return TRUE;
3545 // check the super interfaces
3546 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003547 }
3548 }
LemonBoyafe04662023-08-23 21:08:11 +02003549 }
3550
3551 return FALSE;
3552}
3553
3554/*
Ernie Rael2025af12023-12-12 16:58:00 +01003555 * "instanceof(object, classinfo, ...)" function
LemonBoyafe04662023-08-23 21:08:11 +02003556 */
3557 void
3558f_instanceof(typval_T *argvars, typval_T *rettv)
3559{
3560 typval_T *object_tv = &argvars[0];
3561 typval_T *classinfo_tv = &argvars[1];
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02003562 class_T *c;
LemonBoyafe04662023-08-23 21:08:11 +02003563
3564 rettv->vval.v_number = VVAL_FALSE;
3565
3566 if (check_for_object_arg(argvars, 0) == FAIL
Ernie Rael2025af12023-12-12 16:58:00 +01003567 || check_for_class_or_typealias_args(argvars, 1) == FAIL)
LemonBoyafe04662023-08-23 21:08:11 +02003568 return;
3569
Ernie Rael3da696d2023-09-19 20:14:18 +02003570 if (object_tv->vval.v_object == NULL)
3571 return;
3572
Ernie Rael2025af12023-12-12 16:58:00 +01003573 for (; classinfo_tv->v_type != VAR_UNKNOWN; ++classinfo_tv)
LemonBoyafe04662023-08-23 21:08:11 +02003574 {
Ernie Rael2025af12023-12-12 16:58:00 +01003575 if (classinfo_tv->v_type == VAR_TYPEALIAS)
3576 c = classinfo_tv->vval.v_typealias->ta_type->tt_class;
3577 else
3578 c = classinfo_tv->vval.v_class;
3579
3580 if (class_instance_of(object_tv->vval.v_object->obj_class, c))
LemonBoyafe04662023-08-23 21:08:11 +02003581 {
Ernie Rael2025af12023-12-12 16:58:00 +01003582 rettv->vval.v_number = VVAL_TRUE;
3583 return;
LemonBoyafe04662023-08-23 21:08:11 +02003584 }
3585 }
LemonBoyafe04662023-08-23 21:08:11 +02003586}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00003587
3588#endif // FEAT_EVAL