blob: 885ac0385ca1ebd236779fd491d2996bf41c6a56 [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 Lakshmanan3775f772023-09-01 22:05:45 +020075 garray_T *type_list,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020076 type_T **type_ret,
77 char_u **init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +000078{
79 *varname_end = to_name_end(varname, FALSE);
80 if (*varname == '_' && has_public)
81 {
RestorerZ7fe8f432023-09-24 23:21:24 +020082 semsg(_(e_public_variable_name_cannot_start_with_underscore_str), line);
Bram Moolenaard505d172022-12-18 21:42:55 +000083 return FAIL;
84 }
85
86 char_u *colon = skipwhite(*varname_end);
87 char_u *type_arg = colon;
88 type_T *type = NULL;
89 if (*colon == ':')
90 {
91 if (VIM_ISWHITE(**varname_end))
92 {
93 semsg(_(e_no_white_space_allowed_before_colon_str), varname);
94 return FAIL;
95 }
96 if (!VIM_ISWHITE(colon[1]))
97 {
98 semsg(_(e_white_space_required_after_str_str), ":", varname);
99 return FAIL;
100 }
101 type_arg = skipwhite(colon + 1);
102 type = parse_type(&type_arg, type_list, TRUE);
103 if (type == NULL)
104 return FAIL;
105 }
106
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200107 char_u *init_arg = skipwhite(type_arg);
108 if (type == NULL && *init_arg != '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000109 {
110 emsg(_(e_type_or_initialization_required));
111 return FAIL;
112 }
113
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200114 if (init_expr == NULL && *init_arg == '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000115 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200116 emsg(_(e_cannot_initialize_variable_in_interface));
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200117 return FAIL;
118 }
119
120 if (*init_arg == '=')
121 {
122 evalarg_T evalarg;
123 char_u *expr_start, *expr_end;
124
125 if (!VIM_ISWHITE(init_arg[-1]) || !VIM_ISWHITE(init_arg[1]))
Bram Moolenaard505d172022-12-18 21:42:55 +0000126 {
127 semsg(_(e_white_space_required_before_and_after_str_at_str),
128 "=", type_arg);
129 return FAIL;
130 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200131 init_arg = skipwhite(init_arg + 1);
Bram Moolenaard505d172022-12-18 21:42:55 +0000132
Bram Moolenaard505d172022-12-18 21:42:55 +0000133 fill_evalarg_from_eap(&evalarg, eap, FALSE);
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200134 (void)skip_expr_concatenate(&init_arg, &expr_start, &expr_end, &evalarg);
Bram Moolenaard505d172022-12-18 21:42:55 +0000135
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +0200136 // No type specified for the member. Set it to "any" and the correct
137 // type will be set when the object is instantiated.
Bram Moolenaard505d172022-12-18 21:42:55 +0000138 if (type == NULL)
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200139 type = &t_any;
Bram Moolenaard505d172022-12-18 21:42:55 +0000140
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200141 *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
142 // Free the memory pointed by expr_start.
Bram Moolenaard505d172022-12-18 21:42:55 +0000143 clear_evalarg(&evalarg, NULL);
144 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200145 else if (!valid_declaration_type(type))
Bram Moolenaard505d172022-12-18 21:42:55 +0000146 return FAIL;
147
148 *type_ret = type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000149 return OK;
150}
151
152/*
153 * Add a member to an object or a class.
154 * Returns OK when successful, "init_expr" will be consumed then.
155 * Returns FAIL otherwise, caller might need to free "init_expr".
156 */
157 static int
158add_member(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200159 garray_T *gap,
160 char_u *varname,
161 char_u *varname_end,
162 int has_public,
163 type_T *type,
164 char_u *init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +0000165{
166 if (ga_grow(gap, 1) == FAIL)
167 return FAIL;
168 ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
169 m->ocm_name = vim_strnsave(varname, varname_end - varname);
=?UTF-8?q?Ola=20S=C3=B6der?=d8742472023-03-05 13:12:32 +0000170 m->ocm_access = has_public ? VIM_ACCESS_ALL
171 : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
Bram Moolenaard505d172022-12-18 21:42:55 +0000172 m->ocm_type = type;
173 if (init_expr != NULL)
174 m->ocm_init = init_expr;
175 ++gap->ga_len;
176 return OK;
177}
178
179/*
180 * Move the class or object members found while parsing a class into the class.
181 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000182 * "parent_members" points to the members in the parent class (if any)
183 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000184 * "members" will be set to the newly allocated array of members and
185 * "member_count" set to the number of members.
186 * Returns OK or FAIL.
187 */
188 static int
189add_members_to_class(
190 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000191 ocmember_T *parent_members,
192 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000193 ocmember_T **members,
194 int *member_count)
195{
Bram Moolenaar83677162023-01-08 19:54:10 +0000196 *member_count = parent_count + gap->ga_len;
197 *members = *member_count == 0 ? NULL
198 : ALLOC_MULT(ocmember_T, *member_count);
199 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000200 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000201 for (int i = 0; i < parent_count; ++i)
202 {
203 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000204 ocmember_T *m = *members + i;
205 *m = parent_members[i];
206 m->ocm_name = vim_strsave(m->ocm_name);
207 if (m->ocm_init != NULL)
208 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000209 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000210 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000211 // new members are moved
212 mch_memmove(*members + parent_count,
213 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000214 VIM_CLEAR(gap->ga_data);
215 return OK;
216}
217
218/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000219 * Convert a member index "idx" of interface "itf" to the member index of class
220 * "cl" implementing that interface.
221 */
222 int
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200223object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000224{
Ernie Rael18143d32023-09-04 22:30:41 +0200225 if (idx >= (is_method ? itf->class_obj_method_count
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200226 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000227 {
228 siemsg("index %d out of range for interface %s", idx, itf->class_name);
229 return 0;
230 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200231
232 // If "cl" is the interface or the class that is extended, then the method
233 // index can be used directly and there is no need to search for the method
234 // index in one of the child classes.
235 if (cl == itf)
236 return idx;
237
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200238 itf2class_T *i2c = NULL;
239 int searching = TRUE;
240 int method_offset = 0;
241
Ernie Raelcf138d42023-09-06 20:45:03 +0200242 for (class_T *super = cl; super != NULL && searching;
243 super = super->class_extends)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200244 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200245 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200246 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200247 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
248 {
249 searching = FALSE;
250 break;
251 }
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200252 }
253 if (searching && is_method)
254 // The parent class methods are stored after the current class
255 // methods.
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200256 method_offset += super->class_obj_method_count_child;
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200257 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000258 if (i2c == NULL)
259 {
260 siemsg("class %s not found on interface %s",
261 cl->class_name, itf->class_name);
262 return 0;
263 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +0200264
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200265 // A table follows the i2c for the class
266 int *table = (int *)(i2c + 1);
267 // "method_offset" is 0, if method is in the current class. If method
268 // is in a parent class, then it is non-zero.
269 return table[idx] + method_offset;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000270}
271
272/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200273 * Check whether a class named "extends_name" is present. If the class is
274 * valid, then "extends_clp" is set with the class pointer.
275 * Returns TRUE if the class name "extends_names" is a valid class.
276 */
277 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200278validate_extends_class(
279 char_u *extends_name,
280 class_T **extends_clp,
281 int is_class)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200282{
283 typval_T tv;
284 int success = FALSE;
285
286 tv.v_type = VAR_UNKNOWN;
287 if (eval_variable_import(extends_name, &tv) == FAIL)
288 {
289 semsg(_(e_class_name_not_found_str), extends_name);
290 return success;
291 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200292
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200293 if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
294 || (is_class
295 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
296 || (!is_class
297 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0))
298 // a interface cannot extend a class and a class cannot extend an
299 // interface.
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200300 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200301 else
302 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200303 class_T *extends_cl = tv.vval.v_class;
304 ++extends_cl->class_refcount;
305 *extends_clp = extends_cl;
306 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200307 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200308 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200309
310 return success;
311}
312
313/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200314 * Check method names in the parent class lineage to make sure the access is
315 * the same for overridden methods.
316 */
317 static int
318validate_extends_methods(
319 garray_T *objmethods_gap,
320 class_T *extends_cl)
321{
322 class_T *super = extends_cl;
323 int method_count = objmethods_gap->ga_len;
324 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
325
326 while (super != NULL)
327 {
328 int extends_method_count = super->class_obj_method_count_child;
329 if (extends_method_count == 0)
330 {
331 super = super->class_extends;
332 continue;
333 }
334
335 ufunc_T **extends_methods = super->class_obj_methods;
336
337 for (int i = 0; i < extends_method_count; i++)
338 {
339 char_u *pstr = extends_methods[i]->uf_name;
340 int extends_private = (*pstr == '_');
341 if (extends_private)
342 pstr++;
343
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200344 // When comparing the method names, ignore the access type (public
345 // and private methods are considered the same).
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200346 for (int j = 0; j < method_count; j++)
347 {
348 char_u *qstr = cl_fp[j]->uf_name;
349 int priv_method = (*qstr == '_');
350 if (priv_method)
351 qstr++;
352 if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
353 {
354 // Method access is different between the super class and
355 // the subclass
356 semsg(_(e_method_str_of_class_str_has_different_access),
357 cl_fp[j]->uf_name, super->class_name);
358 return FALSE;
359 }
360 }
361 }
362 super = super->class_extends;
363 }
364
365 return TRUE;
366}
367
368/*
369 * Check whether a object member variable in "objmembers_gap" is a duplicate of
370 * a member in any of the extended parent class lineage. Returns TRUE if there
371 * are no duplicates.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200372 */
373 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200374extends_check_dup_members(
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200375 garray_T *objmembers_gap,
376 class_T *extends_cl)
377{
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200378 int member_count = objmembers_gap->ga_len;
379 if (member_count == 0)
380 return TRUE;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200381
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200382 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
383
384 // Validate each member variable
385 for (int c_i = 0; c_i < member_count; c_i++)
386 {
387 class_T *p_cl = extends_cl;
388 ocmember_T *c_m = members + c_i;
389 char_u *pstr = (*c_m->ocm_name == '_')
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200390 ? c_m->ocm_name + 1 : c_m->ocm_name;
391
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200392 // Check in all the parent classes in the lineage
393 while (p_cl != NULL)
394 {
395 int p_member_count = p_cl->class_obj_member_count;
396 if (p_member_count == 0)
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200397 {
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200398 p_cl = p_cl->class_extends;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200399 continue;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200400 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200401 ocmember_T *p_members = p_cl->class_obj_members;
402
403 // Compare against all the members in the parent class
404 for (int p_i = 0; p_i < p_member_count; p_i++)
405 {
406 ocmember_T *p_m = p_members + p_i;
407 char_u *qstr = (*p_m->ocm_name == '_')
408 ? p_m->ocm_name + 1 : p_m->ocm_name;
409 if (STRCMP(pstr, qstr) == 0)
410 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200411 semsg(_(e_duplicate_variable_str), c_m->ocm_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200412 return FALSE;
413 }
414 }
415
416 p_cl = p_cl->class_extends;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200417 }
418 }
419
420 return TRUE;
421}
422
423/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200424 * Compare the variable type of interface variables in "objmembers_gap" against
425 * the variable in any of the extended super interface lineage. Used to
426 * compare the variable types when extending interfaces. Returns TRUE if the
427 * variable types are the same.
428 */
429 static int
430extends_check_intf_var_type(
431 garray_T *objmembers_gap,
432 class_T *extends_cl)
433{
434 int member_count = objmembers_gap->ga_len;
435 if (member_count == 0)
436 return TRUE;
437
438 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
439
440 // Validate each member variable
441 for (int c_i = 0; c_i < member_count; c_i++)
442 {
443 class_T *p_cl = extends_cl;
444 ocmember_T *c_m = members + c_i;
445 int var_found = FALSE;
446
447 // Check in all the parent classes in the lineage
448 while (p_cl != NULL && !var_found)
449 {
450 int p_member_count = p_cl->class_obj_member_count;
451 if (p_member_count == 0)
452 {
453 p_cl = p_cl->class_extends;
454 continue;
455 }
456 ocmember_T *p_members = p_cl->class_obj_members;
457
458 // Compare against all the members in the parent class
459 for (int p_i = 0; p_i < p_member_count; p_i++)
460 {
461 where_T where = WHERE_INIT;
462 ocmember_T *p_m = p_members + p_i;
463
464 if (STRCMP(p_m->ocm_name, c_m->ocm_name) != 0)
465 continue;
466
467 // Ensure the type is matching.
468 where.wt_func_name = (char *)c_m->ocm_name;
469 where.wt_kind = WT_MEMBER;
470
471 if (check_type(p_m->ocm_type, c_m->ocm_type, TRUE,
472 where) == FAIL)
473 return FALSE;
474
475 var_found = TRUE;
476 }
477
478 p_cl = p_cl->class_extends;
479 }
480 }
481
482 return TRUE;
483}
484
485/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200486 * When extending an abstract class, check whether all the abstract methods in
487 * the parent class are implemented. Returns TRUE if all the methods are
488 * implemented.
489 */
490 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200491validate_abstract_class_methods(
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200492 garray_T *classmethods_gap,
493 garray_T *objmethods_gap,
494 class_T *extends_cl)
495{
496 for (int loop = 1; loop <= 2; ++loop)
497 {
498 // loop == 1: check class methods
499 // loop == 2: check object methods
500 int extends_method_count = loop == 1
501 ? extends_cl->class_class_function_count
502 : extends_cl->class_obj_method_count;
503 if (extends_method_count == 0)
504 continue;
505
506 ufunc_T **extends_methods = loop == 1
507 ? extends_cl->class_class_functions
508 : extends_cl->class_obj_methods;
509
510 int method_count = loop == 1 ? classmethods_gap->ga_len
511 : objmethods_gap->ga_len;
512 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
513 ? classmethods_gap->ga_data
514 : objmethods_gap->ga_data);
515
516 for (int i = 0; i < extends_method_count; i++)
517 {
518 ufunc_T *uf = extends_methods[i];
Yegappan Lakshmanan1db15142023-09-19 20:34:05 +0200519 if (!IS_ABSTRACT_METHOD(uf))
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200520 continue;
521
522 int method_found = FALSE;
523
524 for (int j = 0; j < method_count; j++)
525 {
526 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
527 {
528 method_found = TRUE;
529 break;
530 }
531 }
532
533 if (!method_found)
534 {
535 semsg(_(e_abstract_method_str_not_found), uf->uf_name);
536 return FALSE;
537 }
538 }
539 }
540
541 return TRUE;
542}
543
544/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200545 * Returns TRUE if the interface variable "if_var" is present in the list of
546 * variables in "cl_mt" or in the parent lineage of one of the extended classes
547 * in "extends_cl". For a class variable, 'is_class_var' is TRUE.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200548 */
549 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200550intf_variable_present(
551 char_u *intf_class_name,
552 ocmember_T *if_var,
553 int is_class_var,
554 ocmember_T *cl_mt,
555 int cl_member_count,
556 class_T *extends_cl)
557{
558 int variable_present = FALSE;
559
560 for (int cl_i = 0; cl_i < cl_member_count; ++cl_i)
561 {
562 ocmember_T *m = &cl_mt[cl_i];
563 where_T where = WHERE_INIT;
564
565 if (STRCMP(if_var->ocm_name, m->ocm_name) != 0)
566 continue;
567
568 // Ensure the access type is same
569 if (if_var->ocm_access != m->ocm_access)
570 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200571 semsg(_(e_variable_str_of_interface_str_has_different_access),
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200572 if_var->ocm_name, intf_class_name);
573 return FALSE;
574 }
575
576 // Ensure the type is matching.
577 if (m->ocm_type == &t_any)
578 {
579 // variable type is not specified. Use the variable type in the
580 // interface.
581 m->ocm_type = if_var->ocm_type;
582 }
583 else
584 {
585 where.wt_func_name = (char *)m->ocm_name;
586 where.wt_kind = WT_MEMBER;
587 if (check_type(if_var->ocm_type, m->ocm_type, TRUE,
588 where) == FAIL)
589 return FALSE;
590 }
591
592 variable_present = TRUE;
593 break;
594 }
595
596 if (!variable_present && extends_cl != NULL)
597 {
598 int ext_cl_count = is_class_var
599 ? extends_cl->class_class_member_count
600 : extends_cl->class_obj_member_count;
601 ocmember_T *ext_cl_mt = is_class_var
602 ? extends_cl->class_class_members
603 : extends_cl->class_obj_members;
604 return intf_variable_present(intf_class_name, if_var,
605 is_class_var, ext_cl_mt,
606 ext_cl_count,
607 extends_cl->class_extends);
608 }
609
610 return variable_present;
611}
612
613/*
614 * Check the variables of the interface class "ifcl" match the class variables
615 * ("classmembers_gap") and object variables ("objmembers_gap") of a class.
616 * Returns TRUE if the class and object variables names are valid.
617 */
618 static int
619validate_interface_variables(
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200620 char_u *intf_class_name,
621 class_T *ifcl,
622 garray_T *classmembers_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200623 garray_T *objmembers_gap,
624 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200625{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200626 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200627 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200628 // loop == 1: check class variables
629 // loop == 2: check object variables
630 int is_class_var = (loop == 1);
631 int if_count = is_class_var ? ifcl->class_class_member_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200632 : ifcl->class_obj_member_count;
633 if (if_count == 0)
634 continue;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200635 ocmember_T *if_ms = is_class_var ? ifcl->class_class_members
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200636 : ifcl->class_obj_members;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200637 ocmember_T *cl_ms = (ocmember_T *)(is_class_var
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200638 ? classmembers_gap->ga_data
639 : objmembers_gap->ga_data);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200640 int cl_count = is_class_var ? classmembers_gap->ga_len
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200641 : objmembers_gap->ga_len;
642 for (int if_i = 0; if_i < if_count; ++if_i)
643 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200644 if (!intf_variable_present(intf_class_name, &if_ms[if_i],
645 is_class_var, cl_ms, cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200646 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200647 semsg(_(e_variable_str_of_interface_str_not_implemented),
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200648 if_ms[if_i].ocm_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200649 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200650 }
651 }
652 }
653
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200654 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200655}
656
657/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200658 * Returns TRUE if the method signature of "if_method" and "cl_method" matches.
659 */
660 static int
661intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method)
662{
663 where_T where = WHERE_INIT;
664
665 // Ensure the type is matching.
666 where.wt_func_name = (char *)if_method->uf_name;
667 where.wt_kind = WT_METHOD;
668 if (check_type(if_method->uf_func_type, cl_method->uf_func_type, TRUE,
669 where) == FAIL)
670 return FALSE;
671
672 return TRUE;
673}
674
675/*
676 * Returns TRUE if the interface method "if_ufunc" is present in the list of
677 * methods in "cl_fp" or in the parent lineage of one of the extended classes
678 * in "extends_cl". For a class method, 'is_class_method' is TRUE.
679 */
680 static int
681intf_method_present(
682 ufunc_T *if_ufunc,
683 int is_class_method,
684 ufunc_T **cl_fp,
685 int cl_count,
686 class_T *extends_cl)
687{
688 int method_present = FALSE;
689
690 for (int cl_i = 0; cl_i < cl_count; ++cl_i)
691 {
692 char_u *cl_name = cl_fp[cl_i]->uf_name;
693 if (STRCMP(if_ufunc->uf_name, cl_name) == 0)
694 {
695 // Ensure the type is matching.
696 if (!intf_method_type_matches(if_ufunc, cl_fp[cl_i]))
697 return FALSE;
698 method_present = TRUE;
699 break;
700 }
701 }
702
703 if (!method_present && extends_cl != NULL)
704 {
705 ufunc_T **ext_cl_fp = (ufunc_T **)(is_class_method
706 ? extends_cl->class_class_functions
707 : extends_cl->class_obj_methods);
708 int ext_cl_count = is_class_method
709 ? extends_cl->class_class_function_count
710 : extends_cl->class_obj_method_count;
711 return intf_method_present(if_ufunc, is_class_method, ext_cl_fp,
712 ext_cl_count,
713 extends_cl->class_extends);
714 }
715
716 return method_present;
717}
718
719/*
720 * Validate that a new class implements all the class/instance methods in the
721 * interface "ifcl". The new class methods are in "classfunctions_gap" and the
722 * new object methods are in "objmemthods_gap". Also validates the method
723 * types.
724 * Returns TRUE if all the interface class/object methods are implemented in
725 * the new class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200726 */
727 static int
728validate_interface_methods(
729 char_u *intf_class_name,
730 class_T *ifcl,
731 garray_T *classfunctions_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200732 garray_T *objmethods_gap,
733 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200734{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200735 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200736 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200737 // loop == 1: check class methods
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200738 // loop == 2: check object methods
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200739 int is_class_method = (loop == 1);
740 int if_count = is_class_method ? ifcl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200741 : ifcl->class_obj_method_count;
742 if (if_count == 0)
743 continue;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200744 ufunc_T **if_fp = is_class_method ? ifcl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200745 : ifcl->class_obj_methods;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200746 ufunc_T **cl_fp = (ufunc_T **)(is_class_method
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200747 ? classfunctions_gap->ga_data
748 : objmethods_gap->ga_data);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200749 int cl_count = is_class_method ? classfunctions_gap->ga_len
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200750 : objmethods_gap->ga_len;
751 for (int if_i = 0; if_i < if_count; ++if_i)
752 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200753 char_u *if_name = if_fp[if_i]->uf_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200754
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200755 if (!intf_method_present(if_fp[if_i], is_class_method, cl_fp,
756 cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200757 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200758 semsg(_(e_method_str_of_interface_str_not_implemented),
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200759 if_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200760 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200761 }
762 }
763 }
764
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200765 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200766}
767
768/*
769 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200770 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200771 * object methods and object members in the new class are in
772 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
773 * "objmembers_gap" respectively.
774 */
775 static int
776validate_implements_classes(
777 garray_T *impl_gap,
778 class_T **intf_classes,
779 garray_T *classfunctions_gap,
780 garray_T *classmembers_gap,
781 garray_T *objmethods_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200782 garray_T *objmembers_gap,
783 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200784{
785 int success = TRUE;
786
787 for (int i = 0; i < impl_gap->ga_len && success; ++i)
788 {
789 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
790 typval_T tv;
791 tv.v_type = VAR_UNKNOWN;
792 if (eval_variable_import(impl, &tv) == FAIL)
793 {
794 semsg(_(e_interface_name_not_found_str), impl);
795 success = FALSE;
796 break;
797 }
798
799 if (tv.v_type != VAR_CLASS
800 || tv.vval.v_class == NULL
801 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
802 {
803 semsg(_(e_not_valid_interface_str), impl);
804 success = FALSE;
805 clear_tv(&tv);
806 break;
807 }
808
809 class_T *ifcl = tv.vval.v_class;
810 intf_classes[i] = ifcl;
811 ++ifcl->class_refcount;
812
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200813 // check the variables of the interface match the members of the class
814 success = validate_interface_variables(impl, ifcl, classmembers_gap,
815 objmembers_gap, extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200816
817 // check the functions/methods of the interface match the
818 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200819 if (success)
820 success = validate_interface_methods(impl, ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200821 classfunctions_gap, objmethods_gap,
822 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200823 clear_tv(&tv);
824 }
825
826 return success;
827}
828
829/*
830 * Check no function argument name is used as a class member.
831 * (Object members are always accessed with "this." prefix, so no need
832 * to check them.)
833 */
834 static int
835check_func_arg_names(
836 garray_T *classfunctions_gap,
837 garray_T *objmethods_gap,
838 garray_T *classmembers_gap)
839{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200840 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200841 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200842 {
843 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
844
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200845 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200846 {
847 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
848
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200849 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200850 {
851 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
852 garray_T *mgap = classmembers_gap;
853
854 // Check all the class member names
855 for (int mi = 0; mi < mgap->ga_len; ++mi)
856 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200857 char_u *mname =
858 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200859 if (STRCMP(aname, mname) == 0)
860 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200861 if (uf->uf_script_ctx.sc_sid > 0)
862 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
863
864 semsg(_(e_argument_already_declared_in_class_str),
865 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200866
867 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200868 }
869 }
870 }
871 }
872 }
873
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200874 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200875}
876
877/*
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200878 * Returns TRUE if 'varname' is a reserved keyword name
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200879 */
880 static int
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200881is_reserved_varname(char_u *varname, char_u *varname_end)
882{
883 int reserved = FALSE;
884 char_u save_varname_end = *varname_end;
885 *varname_end = NUL;
886
887 reserved = check_reserved_name(varname, FALSE) == FAIL;
888
889 *varname_end = save_varname_end;
890
891 return reserved;
892}
893
894/*
895 * Returns TRUE if the variable "varname" is already defined either as a class
896 * variable or as an object variable.
897 */
898 static int
899is_duplicate_variable(
900 garray_T *class_members,
901 garray_T *obj_members,
902 char_u *varname,
903 char_u *varname_end)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200904{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200905 char_u *name = vim_strnsave(varname, varname_end - varname);
906 char_u *pstr = (*name == '_') ? name + 1 : name;
907 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200908
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200909 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200910 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200911 // loop == 1: class variables, loop == 2: object variables
912 garray_T *vgap = (loop == 1) ? class_members : obj_members;
913 for (int i = 0; i < vgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200914 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200915 ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i;
916 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1
917 : m->ocm_name;
918 if (STRCMP(pstr, qstr) == 0)
919 {
920 semsg(_(e_duplicate_variable_str), name);
921 dup = TRUE;
922 break;
923 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200924 }
925 }
926
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200927 vim_free(name);
928 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200929}
930
931/*
932 * Returns TRUE if the method "name" is already defined.
933 */
934 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200935is_duplicate_method(
936 garray_T *classmethods_gap,
937 garray_T *objmethods_gap,
938 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200939{
940 char_u *pstr = (*name == '_') ? name + 1 : name;
941
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200942 // loop 1: class methods, loop 2: object methods
943 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200944 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200945 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
946 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200947 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200948 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
949 char_u *qstr = *n == '_' ? n + 1 : n;
950 if (STRCMP(pstr, qstr) == 0)
951 {
952 semsg(_(e_duplicate_function_str), name);
953 return TRUE;
954 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200955 }
956 }
957
958 return FALSE;
959}
960
961/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200962 * Returns TRUE if the constructor is valid.
963 */
964 static int
965is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
966{
967 // Constructors are not allowed in abstract classes.
968 if (is_abstract)
969 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200970 emsg(_(e_cannot_define_new_method_in_abstract_class));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200971 return FALSE;
972 }
973 // A constructor is always static, no need to define it so.
974 if (has_static)
975 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200976 emsg(_(e_cannot_define_new_method_as_static));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200977 return FALSE;
978 }
979 // A return type should not be specified for the new()
980 // constructor method.
981 if (uf->uf_ret_type->tt_type != VAR_VOID)
982 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200983 emsg(_(e_cannot_use_a_return_type_with_new_method));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200984 return FALSE;
985 }
986 return TRUE;
987}
988
989/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200990 * Update the interface class lookup table for the member index on the
991 * interface to the member index in the class implementing the interface.
992 * And a lookup table for the object method index on the interface
993 * to the object method index in the class implementing the interface.
994 * This is also used for updating the lookup table for the extended class
995 * hierarchy.
996 */
997 static int
998update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200999 class_T *ifcl,
1000 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001001 garray_T *objmethods,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001002 int pobj_method_offset)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001003{
1004 if (ifcl == NULL)
1005 return OK;
1006
1007 // Table for members.
1008 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001009 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001010 if (if2cl == NULL)
1011 return FAIL;
1012 if2cl->i2c_next = ifcl->class_itf2class;
1013 ifcl->class_itf2class = if2cl;
1014 if2cl->i2c_class = cl;
1015 if2cl->i2c_is_method = FALSE;
1016
1017 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
1018 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
1019 {
1020 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001021 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001022 {
1023 int *table = (int *)(if2cl + 1);
1024 table[if_i] = cl_i;
1025 break;
1026 }
1027 }
1028
1029 // Table for methods.
1030 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001031 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001032 if (if2cl == NULL)
1033 return FAIL;
1034 if2cl->i2c_next = ifcl->class_itf2class;
1035 ifcl->class_itf2class = if2cl;
1036 if2cl->i2c_class = cl;
1037 if2cl->i2c_is_method = TRUE;
1038
1039 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
1040 {
1041 int done = FALSE;
1042 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
1043 {
1044 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001045 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001046 {
1047 int *table = (int *)(if2cl + 1);
1048 table[if_i] = cl_i;
1049 done = TRUE;
1050 break;
1051 }
1052 }
1053
1054 // extended class object method is not overridden by the child class.
1055 // Keep the method declared in one of the parent classes in the
1056 // lineage.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001057 if (!done)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001058 {
1059 // If "ifcl" is not the immediate parent of "cl", then search in
1060 // the intermediate parent classes.
1061 if (cl->class_extends != ifcl)
1062 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001063 class_T *parent = cl->class_extends;
1064 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001065
1066 while (!done && parent != NULL && parent != ifcl)
1067 {
1068
1069 for (int cl_i = 0;
1070 cl_i < parent->class_obj_method_count_child; ++cl_i)
1071 {
1072 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
1073 parent->class_obj_methods[cl_i]->uf_name)
1074 == 0)
1075 {
1076 int *table = (int *)(if2cl + 1);
1077 table[if_i] = method_offset + cl_i;
1078 done = TRUE;
1079 break;
1080 }
1081 }
1082 method_offset += parent->class_obj_method_count_child;
1083 parent = parent->class_extends;
1084 }
1085 }
1086
1087 if (!done)
1088 {
1089 int *table = (int *)(if2cl + 1);
1090 table[if_i] = pobj_method_offset + if_i;
1091 }
1092 }
1093 }
1094
1095 return OK;
1096}
1097
1098/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001099 * Update the member and object method lookup tables for a new class in the
1100 * interface class.
1101 * For each interface add a lookup table for the member index on the interface
1102 * to the member index in the new class. And a lookup table for the object
1103 * method index on the interface to the object method index in the new class.
1104 */
1105 static int
1106add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
1107{
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001108 // update the lookup table for all the implemented interfaces
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001109 for (int i = 0; i < cl->class_interface_count; ++i)
1110 {
1111 class_T *ifcl = cl->class_interfaces_cl[i];
1112
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001113 // update the lookup table for this interface and all its super
1114 // interfaces.
1115 while (ifcl != NULL)
1116 {
1117 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
1118 0) == FAIL)
1119 return FAIL;
1120 ifcl = ifcl->class_extends;
1121 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001122 }
1123
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001124 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001125 if (extends_cl != NULL)
1126 {
1127 class_T *pclass = extends_cl;
1128 int pobj_method_offset = objmethods_gap->ga_len;
1129
1130 // Update the entire lineage of extended classes.
1131 while (pclass != NULL)
1132 {
1133 if (update_member_method_lookup_table(pclass, cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001134 objmethods_gap, pobj_method_offset) == FAIL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001135 return FAIL;
1136
1137 pobj_method_offset += pclass->class_obj_method_count_child;
1138 pclass = pclass->class_extends;
1139 }
1140 }
1141
1142 return OK;
1143}
1144
1145/*
1146 * Add class members to a new class. Allocate a typval for each class member
1147 * and initialize it.
1148 */
1149 static void
1150add_class_members(class_T *cl, exarg_T *eap)
1151{
1152 // Allocate a typval for each class member and initialize it.
1153 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
1154 cl->class_class_member_count);
1155 if (cl->class_members_tv == NULL)
1156 return;
1157
1158 for (int i = 0; i < cl->class_class_member_count; ++i)
1159 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001160 ocmember_T *m = &cl->class_class_members[i];
1161 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001162 if (m->ocm_init != NULL)
1163 {
1164 typval_T *etv = eval_expr(m->ocm_init, eap);
1165 if (etv != NULL)
1166 {
1167 *tv = *etv;
1168 vim_free(etv);
1169 }
1170 }
1171 else
1172 {
1173 // TODO: proper default value
1174 tv->v_type = m->ocm_type->tt_type;
1175 tv->vval.v_string = NULL;
1176 }
1177 }
1178}
1179
1180/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001181 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001182 */
1183 static void
1184add_default_constructor(
1185 class_T *cl,
1186 garray_T *classfunctions_gap,
1187 garray_T *type_list_gap)
1188{
1189 garray_T fga;
1190
1191 ga_init2(&fga, 1, 1000);
1192 ga_concat(&fga, (char_u *)"new(");
1193 for (int i = 0; i < cl->class_obj_member_count; ++i)
1194 {
1195 if (i > 0)
1196 ga_concat(&fga, (char_u *)", ");
1197 ga_concat(&fga, (char_u *)"this.");
1198 ocmember_T *m = cl->class_obj_members + i;
1199 ga_concat(&fga, (char_u *)m->ocm_name);
1200 ga_concat(&fga, (char_u *)" = v:none");
1201 }
1202 ga_concat(&fga, (char_u *)")\nenddef\n");
1203 ga_append(&fga, NUL);
1204
1205 exarg_T fea;
1206 CLEAR_FIELD(fea);
1207 fea.cmdidx = CMD_def;
1208 fea.cmd = fea.arg = fga.ga_data;
1209
1210 garray_T lines_to_free;
1211 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1212
h-eastb895b0f2023-09-24 15:46:31 +02001213 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
1214 cl->class_obj_members, cl->class_obj_member_count);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001215
1216 ga_clear_strings(&lines_to_free);
1217 vim_free(fga.ga_data);
1218
1219 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1220 {
1221 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001222 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001223 ++classfunctions_gap->ga_len;
1224
1225 nf->uf_flags |= FC_NEW;
1226 nf->uf_ret_type = get_type_ptr(type_list_gap);
1227 if (nf->uf_ret_type != NULL)
1228 {
1229 nf->uf_ret_type->tt_type = VAR_OBJECT;
1230 nf->uf_ret_type->tt_class = cl;
1231 nf->uf_ret_type->tt_argcount = 0;
1232 nf->uf_ret_type->tt_args = NULL;
1233 }
1234 }
1235}
1236
1237/*
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001238 * Add the class methods and object methods to the new class "cl".
1239 * When extending a class "extends_cl", add the instance methods from the
1240 * parent class also.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001241 */
1242 static int
1243add_classfuncs_objmethods(
1244 class_T *cl,
1245 class_T *extends_cl,
1246 garray_T *classfunctions_gap,
1247 garray_T *objmethods_gap)
1248{
1249 // loop 1: class functions, loop 2: object methods
1250 for (int loop = 1; loop <= 2; ++loop)
1251 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001252 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1253 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001254 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001255 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001256 : &cl->class_obj_methods;
1257
1258 int parent_count = 0;
1259 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001260 // Include object methods from the parent.
1261 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001262 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001263 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001264 : extends_cl->class_obj_method_count;
1265
1266 *fcount = parent_count + gap->ga_len;
1267 if (*fcount == 0)
1268 {
1269 *fup = NULL;
1270 continue;
1271 }
1272 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1273 if (*fup == NULL)
1274 return FAIL;
1275
1276 if (gap->ga_len != 0)
1277 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
1278 vim_free(gap->ga_data);
1279 if (loop == 1)
1280 cl->class_class_function_count_child = gap->ga_len;
1281 else
1282 cl->class_obj_method_count_child = gap->ga_len;
1283
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001284 if (loop == 2)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001285 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001286 // Copy instance methods from the parent.
1287
1288 for (int i = 0; i < parent_count; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001289 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001290 // Can't use the same parent function, because "uf_class" is
1291 // different and compilation will have a different result.
1292 // Put them after the functions in the current class, object
1293 // methods may be overruled, then "super.Method()" is used to
1294 // find a method from the parent.
1295 ufunc_T *pf = (extends_cl->class_obj_methods)[i];
1296 (*fup)[gap->ga_len + i] = copy_function(pf);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001297
1298 // If the child class overrides a function from the parent
1299 // the signature must be equal.
1300 char_u *pname = pf->uf_name;
1301 for (int ci = 0; ci < gap->ga_len; ++ci)
1302 {
1303 ufunc_T *cf = (*fup)[ci];
1304 char_u *cname = cf->uf_name;
1305 if (STRCMP(pname, cname) == 0)
1306 {
1307 where_T where = WHERE_INIT;
1308 where.wt_func_name = (char *)pname;
1309 where.wt_kind = WT_METHOD;
1310 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1311 TRUE, where);
1312 }
1313 }
1314 }
1315 }
1316
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001317 // Set the class pointer on all the functions and object methods.
1318 for (int i = 0; i < *fcount; ++i)
1319 {
1320 ufunc_T *fp = (*fup)[i];
1321 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001322 if (i < gap->ga_len)
1323 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001324 if (loop == 2)
1325 fp->uf_flags |= FC_OBJECT;
1326 }
1327 }
1328
1329 return OK;
1330}
1331
1332/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001333 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001334 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001335 */
1336 void
1337ex_class(exarg_T *eap)
1338{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001339 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1340 long start_lnum = SOURCING_LNUM;
1341 char_u *arg = eap->arg;
1342 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001343
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001344 if (is_abstract)
1345 {
1346 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1347 {
1348 semsg(_(e_invalid_argument_str), arg);
1349 return;
1350 }
1351 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001352 is_class = TRUE;
1353 }
1354
1355 if (!current_script_is_vim9()
1356 || (cmdmod.cmod_flags & CMOD_LEGACY)
1357 || !getline_equal(eap->getline, eap->cookie, getsourceline))
1358 {
1359 if (is_class)
1360 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1361 else
1362 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1363 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001364 }
1365
1366 if (!ASCII_ISUPPER(*arg))
1367 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001368 if (is_class)
1369 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1370 else
1371 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1372 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001373 return;
1374 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001375 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1376 if (!IS_WHITE_OR_NUL(*name_end))
1377 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001378 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001379 return;
1380 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001381 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001382
Bram Moolenaara86655a2023-01-12 17:06:27 +00001383 // "export class" gets used when creating the class, don't use "is_export"
1384 // for the items inside the class.
1385 int class_export = is_export;
1386 is_export = FALSE;
1387
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001388 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001389 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001390
Bram Moolenaar83677162023-01-08 19:54:10 +00001391 // Name for "extends BaseClass"
1392 char_u *extends = NULL;
1393
Bram Moolenaar94674f22023-01-06 18:42:20 +00001394 // Names for "implements SomeInterface"
1395 garray_T ga_impl;
1396 ga_init2(&ga_impl, sizeof(char_u *), 5);
1397
1398 arg = skipwhite(name_end);
1399 while (*arg != NUL && *arg != '#' && *arg != '\n')
1400 {
1401 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001402 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001403 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1404 {
1405 if (extends != NULL)
1406 {
1407 emsg(_(e_duplicate_extends));
1408 goto early_ret;
1409 }
1410 arg = skipwhite(arg + 7);
1411 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1412 if (!IS_WHITE_OR_NUL(*end))
1413 {
1414 semsg(_(e_white_space_required_after_name_str), arg);
1415 goto early_ret;
1416 }
1417 extends = vim_strnsave(arg, end - arg);
1418 if (extends == NULL)
1419 goto early_ret;
1420
1421 arg = skipwhite(end + 1);
1422 }
1423 else if (STRNCMP(arg, "implements", 10) == 0
1424 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001425 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001426 if (!is_class)
1427 {
1428 emsg(_(e_interface_cannot_use_implements));
1429 goto early_ret;
1430 }
1431
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001432 if (ga_impl.ga_len > 0)
1433 {
1434 emsg(_(e_duplicate_implements));
1435 goto early_ret;
1436 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001437 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001438
1439 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001440 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001441 char_u *impl_end = find_name_end(arg, NULL, NULL,
1442 FNE_CHECK_START);
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001443 if ((!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1444 || (*impl_end == ','
1445 && !IS_WHITE_OR_NUL(*(impl_end + 1))))
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001446 {
1447 semsg(_(e_white_space_required_after_name_str), arg);
1448 goto early_ret;
1449 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001450 if (impl_end - arg == 0)
1451 {
1452 emsg(_(e_missing_name_after_implements));
1453 goto early_ret;
1454 }
1455
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001456 char_u *iname = vim_strnsave(arg, impl_end - arg);
1457 if (iname == NULL)
1458 goto early_ret;
1459 for (int i = 0; i < ga_impl.ga_len; ++i)
1460 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1461 {
1462 semsg(_(e_duplicate_interface_after_implements_str),
1463 iname);
1464 vim_free(iname);
1465 goto early_ret;
1466 }
1467 if (ga_add_string(&ga_impl, iname) == FAIL)
1468 {
1469 vim_free(iname);
1470 goto early_ret;
1471 }
1472 if (*impl_end != ',')
1473 {
1474 arg = skipwhite(impl_end);
1475 break;
1476 }
1477 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001478 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001479 }
1480 else
1481 {
1482 semsg(_(e_trailing_characters_str), arg);
1483early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001484 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001485 ga_clear_strings(&ga_impl);
1486 return;
1487 }
1488 }
1489
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001490 garray_T type_list; // list of pointers to allocated types
1491 ga_init2(&type_list, sizeof(type_T *), 10);
1492
Bram Moolenaard505d172022-12-18 21:42:55 +00001493 // Growarray with class members declared in the class.
1494 garray_T classmembers;
1495 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1496
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001497 // Growarray with functions declared in the class.
1498 garray_T classfunctions;
1499 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001500
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001501 // Growarray with object members declared in the class.
1502 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001503 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001504
1505 // Growarray with object methods declared in the class.
1506 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001507 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001508
1509 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001510 * Go over the body of the class/interface until "endclass" or
1511 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001512 */
1513 char_u *theline = NULL;
1514 int success = FALSE;
1515 for (;;)
1516 {
1517 vim_free(theline);
1518 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
1519 if (theline == NULL)
1520 break;
1521 char_u *line = skipwhite(theline);
1522
Bram Moolenaar418b5472022-12-20 13:38:22 +00001523 // Skip empty and comment lines.
1524 if (*line == NUL)
1525 continue;
1526 if (*line == '#')
1527 {
1528 if (vim9_bad_comment(line))
1529 break;
1530 continue;
1531 }
1532
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001533 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001534 char *end_name = is_class ? "endclass" : "endinterface";
1535 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001536 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001537 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001538 semsg(_(e_command_cannot_be_shortened_str), line);
1539 else if (*p == '|' || !ends_excmd2(line, p))
1540 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001541 else
1542 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001543 break;
1544 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001545 char *wrong_name = is_class ? "endinterface" : "endclass";
1546 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1547 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001548 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001549 break;
1550 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001551
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001552 int has_public = FALSE;
1553 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001554 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001555 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001556 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001557 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001558 break;
1559 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001560 if (!is_class)
1561 {
Yegappan Lakshmananb90e3bc2023-09-28 23:06:48 +02001562 emsg(_(e_public_variable_not_supported_in_interface));
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001563 break;
1564 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001565 has_public = TRUE;
1566 p = skipwhite(line + 6);
1567
Bram Moolenaard505d172022-12-18 21:42:55 +00001568 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001569 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001570 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001571 break;
1572 }
1573 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001574
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001575 int abstract_method = FALSE;
1576 char_u *pa = p;
1577 if (checkforcmd(&p, "abstract", 3))
1578 {
1579 if (STRNCMP(pa, "abstract", 8) != 0)
1580 {
1581 semsg(_(e_command_cannot_be_shortened_str), pa);
1582 break;
1583 }
1584
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001585 if (!is_class)
1586 // ignore "abstract" in an interface (as all the methods in an
1587 // interface are abstract.
1588 p = skipwhite(pa + 8);
1589 else
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001590 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001591 if (!is_abstract)
1592 {
1593 semsg(_(e_abstract_method_in_concrete_class), pa);
1594 break;
1595 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001596
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001597 abstract_method = TRUE;
1598 p = skipwhite(pa + 8);
1599 if (STRNCMP(p, "def", 3) != 0 && STRNCMP(p, "static", 6) != 0)
1600 {
1601 emsg(_(e_abstract_must_be_followed_by_def_or_static));
1602 break;
1603 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001604 }
1605 }
1606
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001607 int has_static = FALSE;
1608 char_u *ps = p;
1609 if (checkforcmd(&p, "static", 4))
1610 {
1611 if (STRNCMP(ps, "static", 6) != 0)
1612 {
1613 semsg(_(e_command_cannot_be_shortened_str), ps);
1614 break;
1615 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001616
1617 if (!is_class)
1618 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001619 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001620 break;
1621 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001622 has_static = TRUE;
1623 p = skipwhite(ps + 6);
1624 }
1625
Bram Moolenaard505d172022-12-18 21:42:55 +00001626 // object members (public, read access, private):
1627 // "this._varname"
1628 // "this.varname"
1629 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001630 if (STRNCMP(p, "this", 4) == 0)
1631 {
1632 if (p[4] != '.' || !eval_isnamec1(p[5]))
1633 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001634 semsg(_(e_invalid_object_variable_declaration_str), p);
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001635 break;
1636 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001637 if (has_static)
1638 {
1639 emsg(_(e_static_cannot_be_followed_by_this));
1640 break;
1641 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001642 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +00001643 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001644 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001645 char_u *init_expr = NULL;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001646
1647 if (!is_class && *varname == '_')
1648 {
1649 // private variables are not supported in an interface
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001650 semsg(_(e_private_variable_not_supported_in_interface),
1651 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001652 break;
1653 }
1654
Bram Moolenaard505d172022-12-18 21:42:55 +00001655 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001656 &varname_end, &type_list, &type,
1657 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001658 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001659 if (is_reserved_varname(varname, varname_end))
1660 {
1661 vim_free(init_expr);
1662 break;
1663 }
1664 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1665 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001666 {
1667 vim_free(init_expr);
1668 break;
1669 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001670 if (add_member(&objmembers, varname, varname_end,
1671 has_public, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001672 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001673 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001674 break;
1675 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001676 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001677
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001678 // constructors:
1679 // def new()
1680 // enddef
1681 // def newOther()
1682 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001683 // object methods and class functions:
1684 // def SomeMethod()
1685 // enddef
1686 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001687 // enddef
1688 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001689 // def <Tval> someMethod()
1690 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001691 else if (checkforcmd(&p, "def", 3))
1692 {
1693 exarg_T ea;
1694 garray_T lines_to_free;
1695
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001696 if (has_public)
1697 {
1698 // "public" keyword is not supported when defining an object or
1699 // class method
1700 emsg(_(e_public_keyword_not_supported_for_method));
1701 break;
1702 }
1703
1704 if (*p == NUL)
1705 {
1706 // No method name following def
1707 semsg(_(e_not_valid_command_in_class_str), line);
1708 break;
1709 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001710
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001711 CLEAR_FIELD(ea);
1712 ea.cmd = line;
1713 ea.arg = p;
1714 ea.cmdidx = CMD_def;
1715 ea.getline = eap->getline;
1716 ea.cookie = eap->cookie;
1717
1718 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001719 int class_flags;
1720 if (is_class)
1721 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
1722 else
1723 class_flags = CF_INTERFACE;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001724 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02001725 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001726 ga_clear_strings(&lines_to_free);
1727
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001728 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001729 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001730 char_u *name = uf->uf_name;
1731 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001732
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001733 if (!is_class && *name == '_')
1734 {
1735 // private variables are not supported in an interface
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001736 semsg(_(e_private_method_not_supported_in_interface),
1737 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001738 func_clear_free(uf, FALSE);
1739 break;
1740 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001741 if (is_new && !is_valid_constructor(uf, is_abstract,
1742 has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001743 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001744 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001745 break;
1746 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001747
Bram Moolenaar58b40092023-01-11 15:59:05 +00001748 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001749 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001750 {
1751 success = FALSE;
1752 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001753 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001754 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001755
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001756 garray_T *fgap = has_static || is_new
1757 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001758 if (ga_grow(fgap, 1) == OK)
1759 {
1760 if (is_new)
1761 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001762
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001763 if (abstract_method)
1764 uf->uf_flags |= FC_ABSTRACT;
1765
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001766 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1767 ++fgap->ga_len;
1768 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001769 }
1770 }
1771
1772 // class members
1773 else if (has_static)
1774 {
1775 // class members (public, read access, private):
1776 // "static _varname"
1777 // "static varname"
1778 // "public static varname"
1779 char_u *varname = p;
1780 char_u *varname_end = NULL;
1781 type_T *type = NULL;
1782 char_u *init_expr = NULL;
1783 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001784 &varname_end, &type_list, &type,
1785 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001786 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001787 if (is_reserved_varname(varname, varname_end))
1788 {
1789 vim_free(init_expr);
1790 break;
1791 }
1792 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1793 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001794 {
1795 vim_free(init_expr);
1796 break;
1797 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001798 if (add_member(&classmembers, varname, varname_end,
1799 has_public, type, init_expr) == FAIL)
1800 {
1801 vim_free(init_expr);
1802 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001803 }
1804 }
1805
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001806 else
1807 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001808 if (is_class)
1809 semsg(_(e_not_valid_command_in_class_str), line);
1810 else
1811 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001812 break;
1813 }
1814 }
1815 vim_free(theline);
1816
Bram Moolenaar83677162023-01-08 19:54:10 +00001817 class_T *extends_cl = NULL; // class from "extends" argument
1818
1819 /*
1820 * Check a few things before defining the class.
1821 */
1822
1823 // Check the "extends" class is valid.
1824 if (success && extends != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001825 success = validate_extends_class(extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00001826 VIM_CLEAR(extends);
1827
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001828 // Check the new object methods to make sure their access (public or
1829 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001830 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001831 success = validate_extends_methods(&objmethods, extends_cl);
1832
1833 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001834 // variables in the extended class lineage. If an interface is extending
1835 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001836 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001837 {
1838 if (is_class)
1839 success = extends_check_dup_members(&objmembers, extends_cl);
1840 else
1841 success = extends_check_intf_var_type(&objmembers, extends_cl);
1842 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001843
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001844 // When extending an abstract class, make sure all the abstract methods in
1845 // the parent class are implemented. If the current class is an abstract
1846 // class, then there is no need for this check.
1847 if (success && !is_abstract && extends_cl != NULL
1848 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001849 success = validate_abstract_class_methods(&classfunctions,
1850 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001851
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001852 class_T **intf_classes = NULL;
1853
Bram Moolenaar83677162023-01-08 19:54:10 +00001854 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001855 if (success && ga_impl.ga_len > 0)
1856 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001857 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1858
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001859 success = validate_implements_classes(&ga_impl, intf_classes,
1860 &classfunctions, &classmembers,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001861 &objmethods, &objmembers,
1862 extends_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001863 }
1864
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001865 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001866 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001867 success = check_func_arg_names(&classfunctions, &objmethods,
1868 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001869
Bram Moolenaareb533502022-12-14 15:06:11 +00001870 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001871 if (success)
1872 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001873 // "endclass" encountered without failures: Create the class.
1874
Bram Moolenaareb533502022-12-14 15:06:11 +00001875 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001876 if (cl == NULL)
1877 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001878 if (!is_class)
1879 cl->class_flags = CLASS_INTERFACE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001880 else if (is_abstract)
1881 cl->class_flags = CLASS_ABSTRACT;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001882
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001883 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001884 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001885 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001886 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001887
Bram Moolenaard0200c82023-01-28 15:19:40 +00001888 if (extends_cl != NULL)
1889 {
1890 cl->class_extends = extends_cl;
1891 extends_cl->class_flags |= CLASS_EXTENDED;
1892 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001893
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001894 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00001895 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001896 NULL,
1897 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00001898 &cl->class_class_members,
1899 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001900 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001901 extends_cl == NULL ? NULL
1902 : extends_cl->class_obj_members,
1903 extends_cl == NULL ? 0
1904 : extends_cl->class_obj_member_count,
1905 &cl->class_obj_members,
1906 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001907 goto cleanup;
1908
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001909 if (ga_impl.ga_len > 0)
1910 {
1911 // Move the "implements" names into the class.
1912 cl->class_interface_count = ga_impl.ga_len;
1913 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1914 if (cl->class_interfaces == NULL)
1915 goto cleanup;
1916 for (int i = 0; i < ga_impl.ga_len; ++i)
1917 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1918 VIM_CLEAR(ga_impl.ga_data);
1919 ga_impl.ga_len = 0;
1920
Bram Moolenaard0200c82023-01-28 15:19:40 +00001921 cl->class_interfaces_cl = intf_classes;
1922 intf_classes = NULL;
1923 }
1924
1925 if (cl->class_interface_count > 0 || extends_cl != NULL)
1926 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001927 // Add a method and member lookup table to each of the interface
1928 // classes.
1929 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1930 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001931 }
1932
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001933 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001934 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001935 add_class_members(cl, eap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001936
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001937 int have_new = FALSE;
1938 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001939 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001940 {
1941 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1942 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001943 {
1944 have_new = TRUE;
1945 break;
1946 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001947 }
1948
1949 if (have_new)
1950 // The return type of new() is an object of class "cl"
1951 class_func->uf_ret_type->tt_class = cl;
1952 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001953 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001954 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001955
Bram Moolenaar58b40092023-01-11 15:59:05 +00001956 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001957 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1958 &objmethods) == FAIL)
1959 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001960
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001961 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001962 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001963 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001964 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001965 cl->class_type_list = type_list;
1966
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001967 class_created(cl);
1968
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001969 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001970 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001971
1972 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001973 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02001974 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001975 typval_T tv;
1976 tv.v_type = VAR_CLASS;
1977 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001978 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001979 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001980 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001981 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001982 return;
1983 }
1984
1985cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00001986 if (cl != NULL)
1987 {
1988 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001989 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001990 if (cl->class_interfaces != NULL)
1991 {
1992 for (int i = 0; i < cl->class_interface_count; ++i)
1993 vim_free(cl->class_interfaces[i]);
1994 vim_free(cl->class_interfaces);
1995 }
1996 if (cl->class_interfaces_cl != NULL)
1997 {
1998 for (int i = 0; i < cl->class_interface_count; ++i)
1999 class_unref(cl->class_interfaces_cl[i]);
2000 vim_free(cl->class_interfaces_cl);
2001 }
Bram Moolenaareb533502022-12-14 15:06:11 +00002002 vim_free(cl->class_obj_members);
2003 vim_free(cl->class_obj_methods);
2004 vim_free(cl);
2005 }
2006
Bram Moolenaar83677162023-01-08 19:54:10 +00002007 vim_free(extends);
2008 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002009
2010 if (intf_classes != NULL)
2011 {
2012 for (int i = 0; i < ga_impl.ga_len; ++i)
2013 class_unref(intf_classes[i]);
2014 vim_free(intf_classes);
2015 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00002016 ga_clear_strings(&ga_impl);
2017
Bram Moolenaard505d172022-12-18 21:42:55 +00002018 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002019 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002020 garray_T *gap = round == 1 ? &classmembers : &objmembers;
2021 if (gap->ga_len == 0 || gap->ga_data == NULL)
2022 continue;
2023
2024 for (int i = 0; i < gap->ga_len; ++i)
2025 {
2026 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2027 vim_free(m->ocm_name);
2028 vim_free(m->ocm_init);
2029 }
2030 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002031 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002032
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002033 for (int i = 0; i < objmethods.ga_len; ++i)
2034 {
2035 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2036 func_clear_free(uf, FALSE);
2037 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002038 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002039
2040 for (int i = 0; i < classfunctions.ga_len; ++i)
2041 {
2042 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2043 func_clear_free(uf, FALSE);
2044 }
2045 ga_clear(&classfunctions);
2046
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002047 clear_type_list(&type_list);
2048}
2049
2050/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002051 * Find member "name" in class "cl", set "member_idx" to the member index and
2052 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002053 * When "is_object" is TRUE, then look for object members. Otherwise look for
2054 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002055 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002056 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002057 */
2058 type_T *
2059class_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002060 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002061 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002062 char_u *name,
2063 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002064 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002065{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002066 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002067 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002068
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002069 *member_idx = -1; // not found (yet)
2070
2071 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2072 member_idx);
2073 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002074 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002075 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2076 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002077 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002078 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002079
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002080 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002081}
2082
2083/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002084 * Handle ":enum" up to ":endenum".
2085 */
2086 void
2087ex_enum(exarg_T *eap UNUSED)
2088{
2089 // TODO
2090}
2091
2092/*
2093 * Handle ":type".
2094 */
2095 void
2096ex_type(exarg_T *eap UNUSED)
2097{
2098 // TODO
2099}
2100
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002101/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002102 * Returns OK if a member variable named "name" is present in the class "cl".
2103 * Otherwise returns FAIL. If found, the member variable typval is set in
2104 * "rettv". If "is_object" is TRUE, then the object member variable table is
2105 * searched. Otherwise the class member variable table is searched.
2106 */
2107 static int
2108get_member_tv(
2109 class_T *cl,
2110 int is_object,
2111 char_u *name,
2112 size_t namelen,
2113 typval_T *rettv)
2114{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002115 ocmember_T *m;
2116 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002117
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002118 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2119 &m_idx);
2120 if (m == NULL)
2121 return FAIL;
2122
2123 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002124 {
RestorerZ7fe8f432023-09-24 23:21:24 +02002125 semsg(_(e_cannot_access_private_variable_str), m->ocm_name);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002126 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002127 }
2128
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002129 // The object only contains a pointer to the class, the member
2130 // values array follows right after that.
2131 object_T *obj = rettv->vval.v_object;
2132 if (is_object)
2133 {
2134 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2135 copy_tv(tv, rettv);
2136 }
2137 else
2138 copy_tv(&cl->class_members_tv[m_idx], rettv);
2139
2140 object_unref(obj);
2141
2142 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002143}
2144
2145/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002146 * Evaluate what comes after a class:
2147 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002148 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002149 * - class constructor: SomeClass.new()
2150 * - object member: someObject.varname
2151 * - object method: someObject.SomeMethod()
2152 *
2153 * "*arg" points to the '.'.
2154 * "*arg" is advanced to after the member name or method call.
2155 *
2156 * Returns FAIL or OK.
2157 */
2158 int
2159class_object_index(
2160 char_u **arg,
2161 typval_T *rettv,
2162 evalarg_T *evalarg,
2163 int verbose UNUSED) // give error messages
2164{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002165 if (VIM_ISWHITE((*arg)[1]))
2166 {
2167 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
2168 return FAIL;
2169 }
2170
2171 ++*arg;
2172 char_u *name = *arg;
2173 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2174 if (name_end == name)
2175 return FAIL;
2176 size_t len = name_end - name;
2177
Bram Moolenaar552bdca2023-02-17 21:08:50 +00002178 class_T *cl;
2179 if (rettv->v_type == VAR_CLASS)
2180 cl = rettv->vval.v_class;
2181 else // VAR_OBJECT
2182 {
2183 if (rettv->vval.v_object == NULL)
2184 {
2185 emsg(_(e_using_null_object));
2186 return FAIL;
2187 }
2188 cl = rettv->vval.v_object->obj_class;
2189 }
2190
Bram Moolenaard13dd302023-03-11 20:56:35 +00002191 if (cl == NULL)
2192 {
2193 emsg(_(e_incomplete_type));
2194 return FAIL;
2195 }
2196
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002197 if (*name_end == '(')
2198 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002199 ufunc_T *fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002200
Ernie Rael4d00b832023-09-11 19:54:42 +02002201 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002202 if (fp == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002203 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002204 method_not_found_msg(cl, rettv->v_type, name, len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002205 return FAIL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002206 }
2207
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002208 typval_T argvars[MAX_FUNC_ARGS + 1];
2209 int argcount = 0;
2210
2211 if (*fp->uf_name == '_')
2212 {
2213 // Cannot access a private method outside of a class
2214 semsg(_(e_cannot_access_private_method_str), name);
2215 return FAIL;
2216 }
2217
2218 char_u *argp = name_end;
2219 int ret = get_func_arguments(&argp, evalarg, 0,
2220 argvars, &argcount);
2221 if (ret == FAIL)
2222 return FAIL;
2223
2224 funcexe_T funcexe;
2225 CLEAR_FIELD(funcexe);
2226 funcexe.fe_evaluate = TRUE;
2227 if (rettv->v_type == VAR_OBJECT)
2228 {
2229 funcexe.fe_object = rettv->vval.v_object;
2230 ++funcexe.fe_object->obj_refcount;
2231 }
2232
2233 // Clear the class or object after calling the function, in
2234 // case the refcount is one.
2235 typval_T tv_tofree = *rettv;
2236 rettv->v_type = VAR_UNKNOWN;
2237
2238 // Call the user function. Result goes into rettv;
2239 int error = call_user_func_check(fp, argcount, argvars,
2240 rettv, &funcexe, NULL);
2241
2242 // Clear the previous rettv and the arguments.
2243 clear_tv(&tv_tofree);
2244 for (int idx = 0; idx < argcount; ++idx)
2245 clear_tv(&argvars[idx]);
2246
2247 if (error != FCERR_NONE)
2248 {
2249 user_func_error(error, printable_func_name(fp),
2250 funcexe.fe_found_var);
2251 return FAIL;
2252 }
2253 *arg = argp;
2254 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002255 }
2256
2257 else if (rettv->v_type == VAR_OBJECT)
2258 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002259 // Search in the object member variable table and the class member
2260 // variable table.
Yegappan Lakshmanan23c92d92023-09-09 11:33:29 +02002261 if (get_member_tv(cl, TRUE, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002262 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002263 *arg = name_end;
2264 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002265 }
2266
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002267 member_not_found_msg(cl, VAR_OBJECT, name, len);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002268 }
2269
Bram Moolenaard505d172022-12-18 21:42:55 +00002270 else if (rettv->v_type == VAR_CLASS)
2271 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002272 int m_idx;
2273
Bram Moolenaard505d172022-12-18 21:42:55 +00002274 // class member
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002275 ocmember_T *m = class_member_lookup(cl, name, len, &m_idx);
2276 if (m == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002277 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002278 member_not_found_msg(cl, VAR_CLASS, name, len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002279 return FAIL;
Bram Moolenaard505d172022-12-18 21:42:55 +00002280 }
2281
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002282 if (*name == '_')
2283 {
RestorerZ7fe8f432023-09-24 23:21:24 +02002284 semsg(_(e_cannot_access_private_variable_str), m->ocm_name);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002285 return FAIL;
2286 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002287
2288 typval_T *tv = &cl->class_members_tv[m_idx];
2289 copy_tv(tv, rettv);
2290 class_unref(cl);
2291
2292 *arg = name_end;
2293 return OK;
Bram Moolenaard505d172022-12-18 21:42:55 +00002294 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002295
2296 return FAIL;
2297}
2298
2299/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002300 * If "arg" points to a class or object method, return it.
2301 * Otherwise return NULL.
2302 */
2303 ufunc_T *
2304find_class_func(char_u **arg)
2305{
2306 char_u *name = *arg;
2307 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2308 if (name_end == name || *name_end != '.')
2309 return NULL;
2310
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002311 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002312 size_t len = name_end - name;
2313 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002314 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00002315 if (eval_variable(name, (int)len,
2316 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002317 return NULL;
2318 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00002319 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002320
2321 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
2322 : tv.vval.v_object->obj_class;
2323 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002324 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002325 char_u *fname = name_end + 1;
2326 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
2327 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00002328 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002329 len = fname_end - fname;
2330
Ernie Rael4d00b832023-09-11 19:54:42 +02002331 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002332
Bram Moolenaareb533502022-12-14 15:06:11 +00002333fail_after_eval:
2334 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002335 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002336}
2337
2338/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002339 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002340 * Returns -1, if the variable is not found.
2341 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002342 */
2343 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002344class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002345{
Ernie Rael4d00b832023-09-11 19:54:42 +02002346 int idx;
2347 class_member_lookup(cl, name, namelen, &idx);
2348 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002349}
2350
2351/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002352 * Returns a pointer to the class member variable "name" in the class "cl".
2353 * Returns NULL if the variable is not found.
2354 * The member variable index is set in "idx".
2355 */
2356 ocmember_T *
2357class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2358{
Ernie Rael4d00b832023-09-11 19:54:42 +02002359 ocmember_T *ret_m = NULL;
2360 int ret_idx = -1;
2361 for (int i = 0; i < cl->class_class_member_count; ++i)
2362 {
2363 ocmember_T *m = &cl->class_class_members[i];
2364 if (namelen)
2365 {
2366 if (STRNCMP(name, m->ocm_name, namelen) == 0
2367 && m->ocm_name[namelen] == NUL)
2368 {
2369 ret_m = m;
2370 ret_idx = i;
2371 break;
2372 }
2373 }
2374 else if (STRCMP(name, m->ocm_name) == 0)
2375 {
2376 ret_m = m;
2377 ret_idx = i;
2378 break;
2379 }
2380 }
2381 if (idx != NULL)
2382 *idx = ret_idx;
2383 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002384}
2385
2386/*
2387 * Returns the index of class method "name" in the class "cl".
2388 * Returns -1, if the method is not found.
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002389 */
2390 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002391class_method_idx(class_T *cl, char_u *name, size_t namelen)
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002392{
Ernie Rael4d00b832023-09-11 19:54:42 +02002393 int idx;
2394 class_method_lookup(cl, name, namelen, &idx);
2395 return idx;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002396}
2397
2398/*
2399 * Returns a pointer to the class method "name" in class "cl".
2400 * Returns NULL if the method is not found.
2401 * The method index is set in "idx".
2402 */
2403 ufunc_T *
2404class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2405{
Ernie Rael4d00b832023-09-11 19:54:42 +02002406 ufunc_T *ret_fp = NULL;
2407 int ret_idx = -1;
2408 for (int i = 0; i < cl->class_class_function_count; ++i)
2409 {
2410 ufunc_T *fp = cl->class_class_functions[i];
2411 char_u *ufname = (char_u *)fp->uf_name;
2412 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2413 {
2414 ret_fp = fp;
2415 ret_idx = i;
2416 break;
2417 }
2418 }
2419 if (idx != NULL)
2420 *idx = ret_idx;
2421 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002422}
2423
2424/*
2425 * Returns the index of object member variable "name" in the class "cl".
2426 * Returns -1, if the variable is not found.
2427 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
2428 */
2429 int
2430object_member_idx(class_T *cl, char_u *name, size_t namelen)
2431{
Ernie Rael4d00b832023-09-11 19:54:42 +02002432 int idx;
2433 object_member_lookup(cl, name, namelen, &idx);
2434 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002435}
2436
2437/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002438 * Returns a pointer to the object member variable "name" in the class "cl".
2439 * Returns NULL if the variable is not found.
2440 * The object member variable index is set in "idx".
2441 */
2442 ocmember_T *
2443object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2444{
Ernie Rael4d00b832023-09-11 19:54:42 +02002445 ocmember_T *ret_m = NULL;
2446 int ret_idx = -1;
2447 for (int i = 0; i < cl->class_obj_member_count; ++i)
2448 {
2449 ocmember_T *m = &cl->class_obj_members[i];
2450 if (namelen)
2451 {
2452 if (STRNCMP(name, m->ocm_name, namelen) == 0
2453 && m->ocm_name[namelen] == NUL)
2454 {
2455 ret_m = m;
2456 ret_idx = i;
2457 break;
2458 }
2459 }
2460 else if (STRCMP(name, m->ocm_name) == 0)
2461 {
2462 ret_m = m;
2463 ret_idx = i;
2464 break;
2465 }
2466 }
2467 if (idx != NULL)
2468 *idx = ret_idx;
2469 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002470}
2471
2472/*
2473 * Returns the index of object method "name" in the class "cl".
2474 * Returns -1, if the method is not found.
2475 */
2476 int
2477object_method_idx(class_T *cl, char_u *name, size_t namelen)
2478{
Ernie Rael4d00b832023-09-11 19:54:42 +02002479 int idx;
2480 object_method_lookup(cl, name, namelen, &idx);
2481 return idx;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002482}
2483
2484/*
2485 * Returns a pointer to the object method "name" in class "cl".
2486 * Returns NULL if the method is not found.
2487 * The object method index is set in "idx".
2488 */
2489 ufunc_T *
2490object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2491{
Ernie Rael4d00b832023-09-11 19:54:42 +02002492 ufunc_T *ret_fp = NULL;
2493 int ret_idx = -1;
2494 for (int i = 0; i < cl->class_obj_method_count; ++i)
2495 {
2496 ufunc_T *fp = cl->class_obj_methods[i];
2497 // Use a separate pointer to avoid that ASAN complains about
2498 // uf_name[] only being 4 characters.
2499 char_u *ufname = (char_u *)fp->uf_name;
2500 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2501 {
2502 ret_fp = fp;
2503 ret_idx = i;
2504 break;
2505 }
2506 }
2507 if (idx != NULL)
2508 *idx = ret_idx;
2509 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002510}
2511
2512/*
2513 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
2514 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
2515 * object member variable.
2516 *
2517 * Returns a pointer to the member variable structure if variable is found.
2518 * Otherwise returns NULL. The member variable index is set in "*idx".
2519 */
2520 ocmember_T *
2521member_lookup(
2522 class_T *cl,
2523 vartype_T v_type,
2524 char_u *name,
2525 size_t namelen,
2526 int *idx)
2527{
2528 if (v_type == VAR_CLASS)
2529 return class_member_lookup(cl, name, namelen, idx);
2530 else
2531 return object_member_lookup(cl, name, namelen, idx);
2532}
2533
2534/*
2535 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
2536 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
2537 *
2538 * Returns a pointer to the method structure if variable is found.
2539 * Otherwise returns NULL. The method variable index is set in "*idx".
2540 */
2541 ufunc_T *
2542method_lookup(
2543 class_T *cl,
2544 vartype_T v_type,
2545 char_u *name,
2546 size_t namelen,
2547 int *idx)
2548{
2549 if (v_type == VAR_CLASS)
2550 return class_method_lookup(cl, name, namelen, idx);
2551 else
2552 return object_method_lookup(cl, name, namelen, idx);
2553}
2554
2555/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00002556 * Return TRUE if current context "cctx_arg" is inside class "cl".
2557 * Return FALSE if not.
2558 */
2559 int
2560inside_class(cctx_T *cctx_arg, class_T *cl)
2561{
2562 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002563 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002564 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002565 return TRUE;
2566 return FALSE;
2567}
2568
2569/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002570 * Make a copy of an object.
2571 */
2572 void
2573copy_object(typval_T *from, typval_T *to)
2574{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002575 if (from->vval.v_object == NULL)
2576 to->vval.v_object = NULL;
2577 else
2578 {
2579 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002580 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002581 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002582}
2583
2584/*
2585 * Free an object.
2586 */
2587 static void
2588object_clear(object_T *obj)
2589{
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002590 // Avoid a recursive call, it can happen if "obj" has a circular reference.
2591 obj->obj_refcount = INT_MAX;
2592
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002593 class_T *cl = obj->obj_class;
2594
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002595 if (!cl)
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002596 return;
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002597
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002598 // the member values are just after the object structure
2599 typval_T *tv = (typval_T *)(obj + 1);
2600 for (int i = 0; i < cl->class_obj_member_count; ++i)
2601 clear_tv(tv + i);
2602
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002603 // Remove from the list headed by "first_object".
2604 object_cleared(obj);
2605
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002606 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002607 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002608}
2609
2610/*
2611 * Unreference an object.
2612 */
2613 void
2614object_unref(object_T *obj)
2615{
2616 if (obj != NULL && --obj->obj_refcount <= 0)
2617 object_clear(obj);
2618}
2619
2620/*
2621 * Make a copy of a class.
2622 */
2623 void
2624copy_class(typval_T *from, typval_T *to)
2625{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002626 if (from->vval.v_class == NULL)
2627 to->vval.v_class = NULL;
2628 else
2629 {
2630 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002631 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002632 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002633}
2634
2635/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002636 * Free the class "cl" and its contents.
2637 */
2638 static void
2639class_free(class_T *cl)
2640{
2641 // Freeing what the class contains may recursively come back here.
2642 // Clear "class_name" first, if it is NULL the class does not need to
2643 // be freed.
2644 VIM_CLEAR(cl->class_name);
2645
2646 class_unref(cl->class_extends);
2647
2648 for (int i = 0; i < cl->class_interface_count; ++i)
2649 {
2650 vim_free(((char_u **)cl->class_interfaces)[i]);
2651 if (cl->class_interfaces_cl[i] != NULL)
2652 class_unref(cl->class_interfaces_cl[i]);
2653 }
2654 vim_free(cl->class_interfaces);
2655 vim_free(cl->class_interfaces_cl);
2656
2657 itf2class_T *next;
2658 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2659 {
2660 next = i2c->i2c_next;
2661 vim_free(i2c);
2662 }
2663
2664 for (int i = 0; i < cl->class_class_member_count; ++i)
2665 {
2666 ocmember_T *m = &cl->class_class_members[i];
2667 vim_free(m->ocm_name);
2668 vim_free(m->ocm_init);
2669 if (cl->class_members_tv != NULL)
2670 clear_tv(&cl->class_members_tv[i]);
2671 }
2672 vim_free(cl->class_class_members);
2673 vim_free(cl->class_members_tv);
2674
2675 for (int i = 0; i < cl->class_obj_member_count; ++i)
2676 {
2677 ocmember_T *m = &cl->class_obj_members[i];
2678 vim_free(m->ocm_name);
2679 vim_free(m->ocm_init);
2680 }
2681 vim_free(cl->class_obj_members);
2682
2683 for (int i = 0; i < cl->class_class_function_count; ++i)
2684 {
2685 ufunc_T *uf = cl->class_class_functions[i];
2686 func_clear_free(uf, FALSE);
2687 }
2688 vim_free(cl->class_class_functions);
2689
2690 for (int i = 0; i < cl->class_obj_method_count; ++i)
2691 {
2692 ufunc_T *uf = cl->class_obj_methods[i];
2693 func_clear_free(uf, FALSE);
2694 }
2695 vim_free(cl->class_obj_methods);
2696
2697 clear_type_list(&cl->class_type_list);
2698
2699 class_cleared(cl);
2700
2701 vim_free(cl);
2702}
2703
2704/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002705 * Unreference a class. Free it when the reference count goes down to zero.
2706 */
2707 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002708class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002709{
Bram Moolenaard505d172022-12-18 21:42:55 +00002710 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002711 class_free(cl);
2712}
2713
2714/*
2715 * Go through the list of all classes and free items without "copyID".
2716 */
2717 int
2718class_free_nonref(int copyID)
2719{
2720 int did_free = FALSE;
2721
2722 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002723 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002724 next_nonref_class = cl->class_next_used;
2725 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002726 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002727 // Free the class and items it contains.
2728 class_free(cl);
2729 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002730 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002731 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002732
2733 next_nonref_class = NULL;
2734 return did_free;
2735}
2736
2737 int
2738set_ref_in_classes(int copyID)
2739{
2740 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
2741 set_ref_in_item_class(cl, copyID, NULL, NULL);
2742
2743 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002744}
2745
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002746static object_T *first_object = NULL;
2747
2748/*
2749 * Call this function when an object has been created. It will be added to the
2750 * list headed by "first_object".
2751 */
2752 void
2753object_created(object_T *obj)
2754{
2755 if (first_object != NULL)
2756 {
2757 obj->obj_next_used = first_object;
2758 first_object->obj_prev_used = obj;
2759 }
2760 first_object = obj;
2761}
2762
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002763static object_T *next_nonref_obj = NULL;
2764
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002765/*
2766 * Call this function when an object has been cleared and is about to be freed.
2767 * It is removed from the list headed by "first_object".
2768 */
2769 void
2770object_cleared(object_T *obj)
2771{
2772 if (obj->obj_next_used != NULL)
2773 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
2774 if (obj->obj_prev_used != NULL)
2775 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
2776 else if (first_object == obj)
2777 first_object = obj->obj_next_used;
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002778
2779 // update the next object to check if needed
2780 if (obj == next_nonref_obj)
2781 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002782}
2783
2784/*
2785 * Go through the list of all objects and free items without "copyID".
2786 */
2787 int
2788object_free_nonref(int copyID)
2789{
2790 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002791
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002792 for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002793 {
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002794 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002795 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
2796 {
2797 // Free the object and items it contains.
2798 object_clear(obj);
2799 did_free = TRUE;
2800 }
2801 }
2802
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002803 next_nonref_obj = NULL;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002804 return did_free;
2805}
2806
LemonBoyafe04662023-08-23 21:08:11 +02002807/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002808 * Echo a class or object method not found message.
2809 */
2810 void
2811method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
2812{
2813 char_u *method_name = vim_strnsave(name, len);
2814 if ((v_type == VAR_OBJECT)
2815 && (class_method_idx(cl, name, len) >= 0))
2816 {
2817 // If this is a class method, then give a different error
2818 if (*name == '_')
2819 semsg(_(e_cannot_access_private_method_str), method_name);
2820 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002821 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002822 method_name, cl->class_name);
2823 }
2824 else if ((v_type == VAR_CLASS)
2825 && (object_method_idx(cl, name, len) >= 0))
2826 {
2827 // If this is an object method, then give a different error
2828 if (*name == '_')
2829 semsg(_(e_cannot_access_private_method_str), method_name);
2830 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002831 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002832 method_name, cl->class_name);
2833 }
2834 else
2835 semsg(_(e_method_not_found_on_class_str_str), cl->class_name,
2836 method_name);
2837 vim_free(method_name);
2838}
2839
2840/*
2841 * Echo a class or object member not found message.
2842 */
2843 void
2844member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
2845{
2846 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
2847
2848 if (v_type == VAR_OBJECT)
2849 {
2850 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02002851 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002852 varname, cl->class_name);
2853 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002854 semsg(_(e_variable_not_found_on_object_str_str), cl->class_name,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002855 varname);
2856 }
2857 else
2858 {
2859 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02002860 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002861 varname, cl->class_name);
2862 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002863 semsg(_(e_class_variable_str_not_found_in_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002864 varname, cl->class_name);
2865 }
2866 vim_free(varname);
2867}
2868
2869/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002870 * Return TRUE when the class "cl", its base class or one of the implemented
2871 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02002872 */
2873 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002874class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02002875{
2876 if (cl == other_cl)
2877 return TRUE;
2878
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002879 // Recursively check the base classes.
2880 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02002881 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002882 if (cl == other_cl)
2883 return TRUE;
2884 // Check the implemented interfaces and the super interfaces
2885 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002886 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002887 class_T *intf = cl->class_interfaces_cl[i];
2888 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002889 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002890 if (intf == other_cl)
2891 return TRUE;
2892 // check the super interfaces
2893 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002894 }
2895 }
LemonBoyafe04662023-08-23 21:08:11 +02002896 }
2897
2898 return FALSE;
2899}
2900
2901/*
2902 * "instanceof(object, classinfo)" function
2903 */
2904 void
2905f_instanceof(typval_T *argvars, typval_T *rettv)
2906{
2907 typval_T *object_tv = &argvars[0];
2908 typval_T *classinfo_tv = &argvars[1];
2909 listitem_T *li;
2910
2911 rettv->vval.v_number = VVAL_FALSE;
2912
2913 if (check_for_object_arg(argvars, 0) == FAIL
2914 || check_for_class_or_list_arg(argvars, 1) == FAIL)
2915 return;
2916
Ernie Rael3da696d2023-09-19 20:14:18 +02002917 if (object_tv->vval.v_object == NULL)
2918 return;
2919
LemonBoyafe04662023-08-23 21:08:11 +02002920 if (classinfo_tv->v_type == VAR_LIST)
2921 {
2922 FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
2923 {
2924 if (li->li_tv.v_type != VAR_CLASS)
2925 {
2926 emsg(_(e_class_required));
2927 return;
2928 }
2929
2930 if (class_instance_of(object_tv->vval.v_object->obj_class,
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002931 li->li_tv.vval.v_class) == TRUE)
LemonBoyafe04662023-08-23 21:08:11 +02002932 {
2933 rettv->vval.v_number = VVAL_TRUE;
2934 return;
2935 }
2936 }
2937 }
2938 else if (classinfo_tv->v_type == VAR_CLASS)
2939 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002940 rettv->vval.v_number = class_instance_of(object_tv->vval.v_object->obj_class,
2941 classinfo_tv->vval.v_class);
LemonBoyafe04662023-08-23 21:08:11 +02002942 }
2943}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002944
2945#endif // FEAT_EVAL