blob: 14d29c7dac505e0d6d1cb03f86138482fe1ace64 [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 {
82 semsg(_(e_public_member_name_cannot_start_with_underscore_str), line);
83 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 {
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200116 emsg(_(e_cannot_initialize_member_in_interface));
117 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
Ernie Rael18143d32023-09-04 22:30:41 +0200223object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl,
224 int is_static)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000225{
Ernie Rael18143d32023-09-04 22:30:41 +0200226 if (idx >= (is_method ? itf->class_obj_method_count
227 : is_static ? itf->class_class_member_count
Bram Moolenaard0200c82023-01-28 15:19:40 +0000228 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000229 {
230 siemsg("index %d out of range for interface %s", idx, itf->class_name);
231 return 0;
232 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200233
234 // If "cl" is the interface or the class that is extended, then the method
235 // index can be used directly and there is no need to search for the method
236 // index in one of the child classes.
237 if (cl == itf)
238 return idx;
239
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200240 itf2class_T *i2c = NULL;
241 int searching = TRUE;
242 int method_offset = 0;
243
Ernie Raelcf138d42023-09-06 20:45:03 +0200244 for (class_T *super = cl; super != NULL && searching;
245 super = super->class_extends)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200246 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200247 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200248 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200249 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
250 {
251 searching = FALSE;
252 break;
253 }
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200254 }
255 if (searching && is_method)
256 // The parent class methods are stored after the current class
257 // methods.
258 method_offset += is_static
259 ? super->class_class_function_count_child
260 : super->class_obj_method_count_child;
261 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000262 if (i2c == NULL)
263 {
264 siemsg("class %s not found on interface %s",
265 cl->class_name, itf->class_name);
266 return 0;
267 }
Ernie Rael18143d32023-09-04 22:30:41 +0200268 if (is_static)
269 {
270 // TODO: Need a table for fast lookup?
271 char_u *name = itf->class_class_members[idx].ocm_name;
272 for (int i = 0; i < i2c->i2c_class->class_class_member_count; ++i)
273 {
274 ocmember_T *m = &i2c->i2c_class->class_class_members[i];
275 if (STRCMP(name, m->ocm_name) == 0)
276 {
277 return i;
278 }
279 }
280 siemsg("class %s, interface %s, static %s not found",
281 cl->class_name, itf->class_name, name);
282 return 0;
283 }
284 else
285 {
286 // A table follows the i2c for the class
287 int *table = (int *)(i2c + 1);
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200288 // "method_offset" is 0, if method is in the current class. If method
289 // is in a parent class, then it is non-zero.
290 return table[idx] + method_offset;
Ernie Rael18143d32023-09-04 22:30:41 +0200291 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000292}
293
294/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200295 * Check whether a class named "extends_name" is present. If the class is
296 * valid, then "extends_clp" is set with the class pointer.
297 * Returns TRUE if the class name "extends_names" is a valid class.
298 */
299 static int
300validate_extends_class(char_u *extends_name, class_T **extends_clp)
301{
302 typval_T tv;
303 int success = FALSE;
304
305 tv.v_type = VAR_UNKNOWN;
306 if (eval_variable_import(extends_name, &tv) == FAIL)
307 {
308 semsg(_(e_class_name_not_found_str), extends_name);
309 return success;
310 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200311
312 if (tv.v_type != VAR_CLASS
313 || tv.vval.v_class == NULL
314 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
315 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200316 else
317 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200318 class_T *extends_cl = tv.vval.v_class;
319 ++extends_cl->class_refcount;
320 *extends_clp = extends_cl;
321 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200322 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200323 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200324
325 return success;
326}
327
328/*
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200329 * Check whether a class/object member variable in "classmembers_gap" /
330 * "objmembers_gap" is a duplicate of a member in any of the extended parent
331 * class lineage. Returns TRUE if there are no duplicates.
332 */
333 static int
334validate_extends_members(
335 garray_T *classmembers_gap,
336 garray_T *objmembers_gap,
337 class_T *extends_cl)
338{
339 for (int loop = 1; loop <= 2; ++loop)
340 {
341 // loop == 1: check class members
342 // loop == 2: check object members
343 int member_count = loop == 1 ? classmembers_gap->ga_len
344 : objmembers_gap->ga_len;
345 if (member_count == 0)
346 continue;
347 ocmember_T *members = (ocmember_T *)(loop == 1
348 ? classmembers_gap->ga_data
349 : objmembers_gap->ga_data);
350
351 // Validate each member variable
352 for (int c_i = 0; c_i < member_count; c_i++)
353 {
354 class_T *p_cl = extends_cl;
355 ocmember_T *c_m = members + c_i;
356 char_u *pstr = (*c_m->ocm_name == '_')
357 ? c_m->ocm_name + 1 : c_m->ocm_name;
358
359 // Check in all the parent classes in the lineage
360 while (p_cl != NULL)
361 {
362 int p_member_count = loop == 1
363 ? p_cl->class_class_member_count
364 : p_cl->class_obj_member_count;
365 if (p_member_count == 0)
366 continue;
367 ocmember_T *p_members = (loop == 1
368 ? p_cl->class_class_members
369 : p_cl->class_obj_members);
370
371 // Compare against all the members in the parent class
372 for (int p_i = 0; p_i < p_member_count; p_i++)
373 {
374 ocmember_T *p_m = p_members + p_i;
375 char_u *qstr = (*p_m->ocm_name == '_')
376 ? p_m->ocm_name + 1 : p_m->ocm_name;
377 if (STRCMP(pstr, qstr) == 0)
378 {
379 semsg(_(e_duplicate_member_str), c_m->ocm_name);
380 return FALSE;
381 }
382 }
383
384 p_cl = p_cl->class_extends;
385 }
386 }
387 }
388
389 return TRUE;
390}
391
392/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200393 * When extending an abstract class, check whether all the abstract methods in
394 * the parent class are implemented. Returns TRUE if all the methods are
395 * implemented.
396 */
397 static int
398validate_extends_methods(
399 garray_T *classmethods_gap,
400 garray_T *objmethods_gap,
401 class_T *extends_cl)
402{
403 for (int loop = 1; loop <= 2; ++loop)
404 {
405 // loop == 1: check class methods
406 // loop == 2: check object methods
407 int extends_method_count = loop == 1
408 ? extends_cl->class_class_function_count
409 : extends_cl->class_obj_method_count;
410 if (extends_method_count == 0)
411 continue;
412
413 ufunc_T **extends_methods = loop == 1
414 ? extends_cl->class_class_functions
415 : extends_cl->class_obj_methods;
416
417 int method_count = loop == 1 ? classmethods_gap->ga_len
418 : objmethods_gap->ga_len;
419 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
420 ? classmethods_gap->ga_data
421 : objmethods_gap->ga_data);
422
423 for (int i = 0; i < extends_method_count; i++)
424 {
425 ufunc_T *uf = extends_methods[i];
426 if ((uf->uf_flags & FC_ABSTRACT) == 0)
427 continue;
428
429 int method_found = FALSE;
430
431 for (int j = 0; j < method_count; j++)
432 {
433 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
434 {
435 method_found = TRUE;
436 break;
437 }
438 }
439
440 if (!method_found)
441 {
442 semsg(_(e_abstract_method_str_not_found), uf->uf_name);
443 return FALSE;
444 }
445 }
446 }
447
448 return TRUE;
449}
450
451/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200452 * Check the members of the interface class "ifcl" match the class members
453 * ("classmembers_gap") and object members ("objmembers_gap") of a class.
454 * Returns TRUE if the class and object member names are valid.
455 */
456 static int
457validate_interface_members(
458 char_u *intf_class_name,
459 class_T *ifcl,
460 garray_T *classmembers_gap,
461 garray_T *objmembers_gap)
462{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200463 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200464 {
465 // loop == 1: check class members
466 // loop == 2: check object members
467 int if_count = loop == 1 ? ifcl->class_class_member_count
468 : ifcl->class_obj_member_count;
469 if (if_count == 0)
470 continue;
471 ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
472 : ifcl->class_obj_members;
473 ocmember_T *cl_ms = (ocmember_T *)(loop == 1
474 ? classmembers_gap->ga_data
475 : objmembers_gap->ga_data);
476 int cl_count = loop == 1 ? classmembers_gap->ga_len
477 : objmembers_gap->ga_len;
478 for (int if_i = 0; if_i < if_count; ++if_i)
479 {
480 int cl_i;
481 for (cl_i = 0; cl_i < cl_count; ++cl_i)
482 {
483 ocmember_T *m = &cl_ms[cl_i];
484 where_T where = WHERE_INIT;
485
486 if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) != 0)
487 continue;
488
489 // Ensure the type is matching.
490 where.wt_func_name = (char *)m->ocm_name;
491 where.wt_kind = WT_MEMBER;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200492 if (check_type(if_ms[if_i].ocm_type, m->ocm_type, TRUE,
493 where) == FAIL)
494 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200495
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200496 if (if_ms[if_i].ocm_access != m->ocm_access)
497 {
498 semsg(_(e_member_str_of_interface_str_has_different_access),
499 if_ms[if_i].ocm_name, intf_class_name);
500 return FALSE;
501 }
502
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200503 break;
504 }
505 if (cl_i == cl_count)
506 {
507 semsg(_(e_member_str_of_interface_str_not_implemented),
508 if_ms[if_i].ocm_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200509 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200510 }
511 }
512 }
513
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200514 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200515}
516
517/*
518 * Check the functions/methods of the interface class "ifcl" match the class
519 * methods ("classfunctions_gap") and object functions ("objmemthods_gap") of a
520 * class.
521 * Returns TRUE if the class and object member names are valid.
522 */
523 static int
524validate_interface_methods(
525 char_u *intf_class_name,
526 class_T *ifcl,
527 garray_T *classfunctions_gap,
528 garray_T *objmethods_gap)
529{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200530 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200531 {
532 // loop == 1: check class functions
533 // loop == 2: check object methods
534 int if_count = loop == 1 ? ifcl->class_class_function_count
535 : ifcl->class_obj_method_count;
536 if (if_count == 0)
537 continue;
538 ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
539 : ifcl->class_obj_methods;
540 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
541 ? classfunctions_gap->ga_data
542 : objmethods_gap->ga_data);
543 int cl_count = loop == 1 ? classfunctions_gap->ga_len
544 : objmethods_gap->ga_len;
545 for (int if_i = 0; if_i < if_count; ++if_i)
546 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200547 char_u *if_name = if_fp[if_i]->uf_name;
548 int cl_i;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200549 for (cl_i = 0; cl_i < cl_count; ++cl_i)
550 {
551 char_u *cl_name = cl_fp[cl_i]->uf_name;
552 if (STRCMP(if_name, cl_name) == 0)
553 {
554 where_T where = WHERE_INIT;
555
556 // Ensure the type is matching.
557 where.wt_func_name = (char *)if_name;
558 where.wt_kind = WT_METHOD;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200559 if (check_type(if_fp[if_i]->uf_func_type,
560 cl_fp[cl_i]->uf_func_type, TRUE, where) == FAIL)
561 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200562 break;
563 }
564 }
565 if (cl_i == cl_count)
566 {
567 semsg(_(e_function_str_of_interface_str_not_implemented),
568 if_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200569 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200570 }
571 }
572 }
573
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200574 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200575}
576
577/*
578 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200579 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200580 * object methods and object members in the new class are in
581 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
582 * "objmembers_gap" respectively.
583 */
584 static int
585validate_implements_classes(
586 garray_T *impl_gap,
587 class_T **intf_classes,
588 garray_T *classfunctions_gap,
589 garray_T *classmembers_gap,
590 garray_T *objmethods_gap,
591 garray_T *objmembers_gap)
592{
593 int success = TRUE;
594
595 for (int i = 0; i < impl_gap->ga_len && success; ++i)
596 {
597 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
598 typval_T tv;
599 tv.v_type = VAR_UNKNOWN;
600 if (eval_variable_import(impl, &tv) == FAIL)
601 {
602 semsg(_(e_interface_name_not_found_str), impl);
603 success = FALSE;
604 break;
605 }
606
607 if (tv.v_type != VAR_CLASS
608 || tv.vval.v_class == NULL
609 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
610 {
611 semsg(_(e_not_valid_interface_str), impl);
612 success = FALSE;
613 clear_tv(&tv);
614 break;
615 }
616
617 class_T *ifcl = tv.vval.v_class;
618 intf_classes[i] = ifcl;
619 ++ifcl->class_refcount;
620
621 // check the members of the interface match the members of the class
622 success = validate_interface_members(impl, ifcl, classmembers_gap,
623 objmembers_gap);
624
625 // check the functions/methods of the interface match the
626 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200627 if (success)
628 success = validate_interface_methods(impl, ifcl,
629 classfunctions_gap, objmethods_gap);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200630 clear_tv(&tv);
631 }
632
633 return success;
634}
635
636/*
637 * Check no function argument name is used as a class member.
638 * (Object members are always accessed with "this." prefix, so no need
639 * to check them.)
640 */
641 static int
642check_func_arg_names(
643 garray_T *classfunctions_gap,
644 garray_T *objmethods_gap,
645 garray_T *classmembers_gap)
646{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200647 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200648 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200649 {
650 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
651
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200652 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200653 {
654 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
655
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200656 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200657 {
658 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
659 garray_T *mgap = classmembers_gap;
660
661 // Check all the class member names
662 for (int mi = 0; mi < mgap->ga_len; ++mi)
663 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200664 char_u *mname =
665 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200666 if (STRCMP(aname, mname) == 0)
667 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200668 if (uf->uf_script_ctx.sc_sid > 0)
669 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
670
671 semsg(_(e_argument_already_declared_in_class_str),
672 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200673
674 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200675 }
676 }
677 }
678 }
679 }
680
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200681 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200682}
683
684/*
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200685 * Returns TRUE if the member "varname" is already defined.
686 */
687 static int
688is_duplicate_member(garray_T *mgap, char_u *varname, char_u *varname_end)
689{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200690 char_u *name = vim_strnsave(varname, varname_end - varname);
691 char_u *pstr = (*name == '_') ? name + 1 : name;
692 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200693
694 for (int i = 0; i < mgap->ga_len; ++i)
695 {
696 ocmember_T *m = ((ocmember_T *)mgap->ga_data) + i;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200697 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1 : m->ocm_name;
698 if (STRCMP(pstr, qstr) == 0)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200699 {
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200700 semsg(_(e_duplicate_member_str), name);
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200701 dup = TRUE;
702 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200703 }
704 }
705
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200706 vim_free(name);
707 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200708}
709
710/*
711 * Returns TRUE if the method "name" is already defined.
712 */
713 static int
714is_duplicate_method(garray_T *fgap, char_u *name)
715{
716 char_u *pstr = (*name == '_') ? name + 1 : name;
717
718 for (int i = 0; i < fgap->ga_len; ++i)
719 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200720 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
721 char_u *qstr = *n == '_' ? n + 1 : n;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200722 if (STRCMP(pstr, qstr) == 0)
723 {
724 semsg(_(e_duplicate_function_str), name);
725 return TRUE;
726 }
727 }
728
729 return FALSE;
730}
731
732/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200733 * Returns TRUE if the constructor is valid.
734 */
735 static int
736is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
737{
738 // Constructors are not allowed in abstract classes.
739 if (is_abstract)
740 {
741 emsg(_(e_cannot_define_new_function_in_abstract_class));
742 return FALSE;
743 }
744 // A constructor is always static, no need to define it so.
745 if (has_static)
746 {
747 emsg(_(e_cannot_define_new_function_as_static));
748 return FALSE;
749 }
750 // A return type should not be specified for the new()
751 // constructor method.
752 if (uf->uf_ret_type->tt_type != VAR_VOID)
753 {
754 emsg(_(e_cannot_use_a_return_type_with_new));
755 return FALSE;
756 }
757 return TRUE;
758}
759
760/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200761 * Update the interface class lookup table for the member index on the
762 * interface to the member index in the class implementing the interface.
763 * And a lookup table for the object method index on the interface
764 * to the object method index in the class implementing the interface.
765 * This is also used for updating the lookup table for the extended class
766 * hierarchy.
767 */
768 static int
769update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200770 class_T *ifcl,
771 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200772 garray_T *objmethods,
773 int pobj_method_offset,
774 int is_interface)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200775{
776 if (ifcl == NULL)
777 return OK;
778
779 // Table for members.
780 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200781 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200782 if (if2cl == NULL)
783 return FAIL;
784 if2cl->i2c_next = ifcl->class_itf2class;
785 ifcl->class_itf2class = if2cl;
786 if2cl->i2c_class = cl;
787 if2cl->i2c_is_method = FALSE;
788
789 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
790 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
791 {
792 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200793 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200794 {
795 int *table = (int *)(if2cl + 1);
796 table[if_i] = cl_i;
797 break;
798 }
799 }
800
801 // Table for methods.
802 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200803 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200804 if (if2cl == NULL)
805 return FAIL;
806 if2cl->i2c_next = ifcl->class_itf2class;
807 ifcl->class_itf2class = if2cl;
808 if2cl->i2c_class = cl;
809 if2cl->i2c_is_method = TRUE;
810
811 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
812 {
813 int done = FALSE;
814 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
815 {
816 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200817 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200818 {
819 int *table = (int *)(if2cl + 1);
820 table[if_i] = cl_i;
821 done = TRUE;
822 break;
823 }
824 }
825
826 // extended class object method is not overridden by the child class.
827 // Keep the method declared in one of the parent classes in the
828 // lineage.
829 if (!done && !is_interface)
830 {
831 // If "ifcl" is not the immediate parent of "cl", then search in
832 // the intermediate parent classes.
833 if (cl->class_extends != ifcl)
834 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200835 class_T *parent = cl->class_extends;
836 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200837
838 while (!done && parent != NULL && parent != ifcl)
839 {
840
841 for (int cl_i = 0;
842 cl_i < parent->class_obj_method_count_child; ++cl_i)
843 {
844 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
845 parent->class_obj_methods[cl_i]->uf_name)
846 == 0)
847 {
848 int *table = (int *)(if2cl + 1);
849 table[if_i] = method_offset + cl_i;
850 done = TRUE;
851 break;
852 }
853 }
854 method_offset += parent->class_obj_method_count_child;
855 parent = parent->class_extends;
856 }
857 }
858
859 if (!done)
860 {
861 int *table = (int *)(if2cl + 1);
862 table[if_i] = pobj_method_offset + if_i;
863 }
864 }
865 }
866
867 return OK;
868}
869
870/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200871 * Update the member and object method lookup tables for a new class in the
872 * interface class.
873 * For each interface add a lookup table for the member index on the interface
874 * to the member index in the new class. And a lookup table for the object
875 * method index on the interface to the object method index in the new class.
876 */
877 static int
878add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
879{
880 for (int i = 0; i < cl->class_interface_count; ++i)
881 {
882 class_T *ifcl = cl->class_interfaces_cl[i];
883
884 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
885 0, TRUE) == FAIL)
886 return FAIL;
887 }
888
889 // Update the lookup table for the extended class, if nay
890 if (extends_cl != NULL)
891 {
892 class_T *pclass = extends_cl;
893 int pobj_method_offset = objmethods_gap->ga_len;
894
895 // Update the entire lineage of extended classes.
896 while (pclass != NULL)
897 {
898 if (update_member_method_lookup_table(pclass, cl,
899 objmethods_gap, pobj_method_offset, FALSE) == FAIL)
900 return FAIL;
901
902 pobj_method_offset += pclass->class_obj_method_count_child;
903 pclass = pclass->class_extends;
904 }
905 }
906
907 return OK;
908}
909
910/*
911 * Add class members to a new class. Allocate a typval for each class member
912 * and initialize it.
913 */
914 static void
915add_class_members(class_T *cl, exarg_T *eap)
916{
917 // Allocate a typval for each class member and initialize it.
918 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
919 cl->class_class_member_count);
920 if (cl->class_members_tv == NULL)
921 return;
922
923 for (int i = 0; i < cl->class_class_member_count; ++i)
924 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200925 ocmember_T *m = &cl->class_class_members[i];
926 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200927 if (m->ocm_init != NULL)
928 {
929 typval_T *etv = eval_expr(m->ocm_init, eap);
930 if (etv != NULL)
931 {
932 *tv = *etv;
933 vim_free(etv);
934 }
935 }
936 else
937 {
938 // TODO: proper default value
939 tv->v_type = m->ocm_type->tt_type;
940 tv->vval.v_string = NULL;
941 }
942 }
943}
944
945/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +0200946 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200947 */
948 static void
949add_default_constructor(
950 class_T *cl,
951 garray_T *classfunctions_gap,
952 garray_T *type_list_gap)
953{
954 garray_T fga;
955
956 ga_init2(&fga, 1, 1000);
957 ga_concat(&fga, (char_u *)"new(");
958 for (int i = 0; i < cl->class_obj_member_count; ++i)
959 {
960 if (i > 0)
961 ga_concat(&fga, (char_u *)", ");
962 ga_concat(&fga, (char_u *)"this.");
963 ocmember_T *m = cl->class_obj_members + i;
964 ga_concat(&fga, (char_u *)m->ocm_name);
965 ga_concat(&fga, (char_u *)" = v:none");
966 }
967 ga_concat(&fga, (char_u *)")\nenddef\n");
968 ga_append(&fga, NUL);
969
970 exarg_T fea;
971 CLEAR_FIELD(fea);
972 fea.cmdidx = CMD_def;
973 fea.cmd = fea.arg = fga.ga_data;
974
975 garray_T lines_to_free;
976 ga_init2(&lines_to_free, sizeof(char_u *), 50);
977
978 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS);
979
980 ga_clear_strings(&lines_to_free);
981 vim_free(fga.ga_data);
982
983 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
984 {
985 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200986 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200987 ++classfunctions_gap->ga_len;
988
989 nf->uf_flags |= FC_NEW;
990 nf->uf_ret_type = get_type_ptr(type_list_gap);
991 if (nf->uf_ret_type != NULL)
992 {
993 nf->uf_ret_type->tt_type = VAR_OBJECT;
994 nf->uf_ret_type->tt_class = cl;
995 nf->uf_ret_type->tt_argcount = 0;
996 nf->uf_ret_type->tt_args = NULL;
997 }
998 }
999}
1000
1001/*
1002 * Add the class functions and object methods to the new class "cl".
1003 * When extending a class, add the functions and methods from the parent class
1004 * also.
1005 */
1006 static int
1007add_classfuncs_objmethods(
1008 class_T *cl,
1009 class_T *extends_cl,
1010 garray_T *classfunctions_gap,
1011 garray_T *objmethods_gap)
1012{
1013 // loop 1: class functions, loop 2: object methods
1014 for (int loop = 1; loop <= 2; ++loop)
1015 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001016 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1017 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001018 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001019 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001020 : &cl->class_obj_methods;
1021
1022 int parent_count = 0;
1023 if (extends_cl != NULL)
1024 // Include functions from the parent.
1025 parent_count = loop == 1
1026 ? extends_cl->class_class_function_count
1027 : extends_cl->class_obj_method_count;
1028
1029 *fcount = parent_count + gap->ga_len;
1030 if (*fcount == 0)
1031 {
1032 *fup = NULL;
1033 continue;
1034 }
1035 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1036 if (*fup == NULL)
1037 return FAIL;
1038
1039 if (gap->ga_len != 0)
1040 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
1041 vim_free(gap->ga_data);
1042 if (loop == 1)
1043 cl->class_class_function_count_child = gap->ga_len;
1044 else
1045 cl->class_obj_method_count_child = gap->ga_len;
1046
1047 int skipped = 0;
1048 for (int i = 0; i < parent_count; ++i)
1049 {
1050 // Copy functions from the parent. Can't use the same
1051 // function, because "uf_class" is different and compilation
1052 // will have a different result.
1053 // Put them after the functions in the current class, object
1054 // methods may be overruled, then "super.Method()" is used to
1055 // find a method from the parent.
1056 // Skip "new" functions. TODO: not all of them.
1057 if (loop == 1 && STRNCMP(
1058 extends_cl->class_class_functions[i]->uf_name,
1059 "new", 3) == 0)
1060 ++skipped;
1061 else
1062 {
1063 ufunc_T *pf = (loop == 1
1064 ? extends_cl->class_class_functions
1065 : extends_cl->class_obj_methods)[i];
1066 (*fup)[gap->ga_len + i - skipped] = copy_function(pf);
1067
1068 // If the child class overrides a function from the parent
1069 // the signature must be equal.
1070 char_u *pname = pf->uf_name;
1071 for (int ci = 0; ci < gap->ga_len; ++ci)
1072 {
1073 ufunc_T *cf = (*fup)[ci];
1074 char_u *cname = cf->uf_name;
1075 if (STRCMP(pname, cname) == 0)
1076 {
1077 where_T where = WHERE_INIT;
1078 where.wt_func_name = (char *)pname;
1079 where.wt_kind = WT_METHOD;
1080 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1081 TRUE, where);
1082 }
1083 }
1084 }
1085 }
1086
1087 *fcount -= skipped;
1088
1089 // Set the class pointer on all the functions and object methods.
1090 for (int i = 0; i < *fcount; ++i)
1091 {
1092 ufunc_T *fp = (*fup)[i];
1093 fp->uf_class = cl;
1094 if (loop == 2)
1095 fp->uf_flags |= FC_OBJECT;
1096 }
1097 }
1098
1099 return OK;
1100}
1101
1102/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001103 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001104 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001105 */
1106 void
1107ex_class(exarg_T *eap)
1108{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001109 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1110 long start_lnum = SOURCING_LNUM;
1111 char_u *arg = eap->arg;
1112 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001113
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001114 if (is_abstract)
1115 {
1116 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1117 {
1118 semsg(_(e_invalid_argument_str), arg);
1119 return;
1120 }
1121 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001122 is_class = TRUE;
1123 }
1124
1125 if (!current_script_is_vim9()
1126 || (cmdmod.cmod_flags & CMOD_LEGACY)
1127 || !getline_equal(eap->getline, eap->cookie, getsourceline))
1128 {
1129 if (is_class)
1130 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1131 else
1132 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1133 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001134 }
1135
1136 if (!ASCII_ISUPPER(*arg))
1137 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001138 if (is_class)
1139 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1140 else
1141 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1142 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001143 return;
1144 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001145 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1146 if (!IS_WHITE_OR_NUL(*name_end))
1147 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001148 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001149 return;
1150 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001151 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001152
Bram Moolenaara86655a2023-01-12 17:06:27 +00001153 // "export class" gets used when creating the class, don't use "is_export"
1154 // for the items inside the class.
1155 int class_export = is_export;
1156 is_export = FALSE;
1157
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001158 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001159 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001160
Bram Moolenaar83677162023-01-08 19:54:10 +00001161 // Name for "extends BaseClass"
1162 char_u *extends = NULL;
1163
Bram Moolenaar94674f22023-01-06 18:42:20 +00001164 // Names for "implements SomeInterface"
1165 garray_T ga_impl;
1166 ga_init2(&ga_impl, sizeof(char_u *), 5);
1167
1168 arg = skipwhite(name_end);
1169 while (*arg != NUL && *arg != '#' && *arg != '\n')
1170 {
1171 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001172 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001173 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1174 {
1175 if (extends != NULL)
1176 {
1177 emsg(_(e_duplicate_extends));
1178 goto early_ret;
1179 }
1180 arg = skipwhite(arg + 7);
1181 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1182 if (!IS_WHITE_OR_NUL(*end))
1183 {
1184 semsg(_(e_white_space_required_after_name_str), arg);
1185 goto early_ret;
1186 }
1187 extends = vim_strnsave(arg, end - arg);
1188 if (extends == NULL)
1189 goto early_ret;
1190
1191 arg = skipwhite(end + 1);
1192 }
1193 else if (STRNCMP(arg, "implements", 10) == 0
1194 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001195 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001196 if (ga_impl.ga_len > 0)
1197 {
1198 emsg(_(e_duplicate_implements));
1199 goto early_ret;
1200 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001201 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001202
1203 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001204 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001205 char_u *impl_end = find_name_end(arg, NULL, NULL,
1206 FNE_CHECK_START);
1207 if (!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1208 {
1209 semsg(_(e_white_space_required_after_name_str), arg);
1210 goto early_ret;
1211 }
1212 char_u *iname = vim_strnsave(arg, impl_end - arg);
1213 if (iname == NULL)
1214 goto early_ret;
1215 for (int i = 0; i < ga_impl.ga_len; ++i)
1216 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1217 {
1218 semsg(_(e_duplicate_interface_after_implements_str),
1219 iname);
1220 vim_free(iname);
1221 goto early_ret;
1222 }
1223 if (ga_add_string(&ga_impl, iname) == FAIL)
1224 {
1225 vim_free(iname);
1226 goto early_ret;
1227 }
1228 if (*impl_end != ',')
1229 {
1230 arg = skipwhite(impl_end);
1231 break;
1232 }
1233 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001234 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001235 }
1236 else
1237 {
1238 semsg(_(e_trailing_characters_str), arg);
1239early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001240 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001241 ga_clear_strings(&ga_impl);
1242 return;
1243 }
1244 }
1245
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001246 garray_T type_list; // list of pointers to allocated types
1247 ga_init2(&type_list, sizeof(type_T *), 10);
1248
Bram Moolenaard505d172022-12-18 21:42:55 +00001249 // Growarray with class members declared in the class.
1250 garray_T classmembers;
1251 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1252
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001253 // Growarray with functions declared in the class.
1254 garray_T classfunctions;
1255 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001256
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001257 // Growarray with object members declared in the class.
1258 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001259 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001260
1261 // Growarray with object methods declared in the class.
1262 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001263 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001264
1265 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001266 * Go over the body of the class/interface until "endclass" or
1267 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001268 */
1269 char_u *theline = NULL;
1270 int success = FALSE;
1271 for (;;)
1272 {
1273 vim_free(theline);
1274 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
1275 if (theline == NULL)
1276 break;
1277 char_u *line = skipwhite(theline);
1278
Bram Moolenaar418b5472022-12-20 13:38:22 +00001279 // Skip empty and comment lines.
1280 if (*line == NUL)
1281 continue;
1282 if (*line == '#')
1283 {
1284 if (vim9_bad_comment(line))
1285 break;
1286 continue;
1287 }
1288
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001289 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001290 char *end_name = is_class ? "endclass" : "endinterface";
1291 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001292 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001293 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001294 semsg(_(e_command_cannot_be_shortened_str), line);
1295 else if (*p == '|' || !ends_excmd2(line, p))
1296 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001297 else
1298 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001299 break;
1300 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001301 char *wrong_name = is_class ? "endinterface" : "endclass";
1302 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1303 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001304 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001305 break;
1306 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001307
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001308 int has_public = FALSE;
1309 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001310 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001311 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001312 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001313 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001314 break;
1315 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001316 has_public = TRUE;
1317 p = skipwhite(line + 6);
1318
Bram Moolenaard505d172022-12-18 21:42:55 +00001319 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001320 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001321 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001322 break;
1323 }
1324 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001325
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001326 int abstract_method = FALSE;
1327 char_u *pa = p;
1328 if (checkforcmd(&p, "abstract", 3))
1329 {
1330 if (STRNCMP(pa, "abstract", 8) != 0)
1331 {
1332 semsg(_(e_command_cannot_be_shortened_str), pa);
1333 break;
1334 }
1335
1336 if (!is_abstract)
1337 {
1338 semsg(_(e_abstract_method_in_concrete_class), pa);
1339 break;
1340 }
1341
1342 abstract_method = TRUE;
1343 p = skipwhite(pa + 8);
1344 if (STRNCMP(p, "def", 3) != 0 && STRNCMP(p, "static", 6) != 0)
1345 {
1346 emsg(_(e_abstract_must_be_followed_by_def_or_static));
1347 break;
1348 }
1349 }
1350
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001351 int has_static = FALSE;
1352 char_u *ps = p;
1353 if (checkforcmd(&p, "static", 4))
1354 {
1355 if (STRNCMP(ps, "static", 6) != 0)
1356 {
1357 semsg(_(e_command_cannot_be_shortened_str), ps);
1358 break;
1359 }
1360 has_static = TRUE;
1361 p = skipwhite(ps + 6);
1362 }
1363
Bram Moolenaard505d172022-12-18 21:42:55 +00001364 // object members (public, read access, private):
1365 // "this._varname"
1366 // "this.varname"
1367 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001368 if (STRNCMP(p, "this", 4) == 0)
1369 {
1370 if (p[4] != '.' || !eval_isnamec1(p[5]))
1371 {
1372 semsg(_(e_invalid_object_member_declaration_str), p);
1373 break;
1374 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001375 if (has_static)
1376 {
1377 emsg(_(e_static_cannot_be_followed_by_this));
1378 break;
1379 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001380 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +00001381 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001382 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001383 char_u *init_expr = NULL;
1384 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001385 &varname_end, &type_list, &type,
1386 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001387 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001388 if (is_duplicate_member(&objmembers, varname, varname_end))
1389 {
1390 vim_free(init_expr);
1391 break;
1392 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001393 if (add_member(&objmembers, varname, varname_end,
1394 has_public, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001395 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001396 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001397 break;
1398 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001399 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001400
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001401 // constructors:
1402 // def new()
1403 // enddef
1404 // def newOther()
1405 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001406 // object methods and class functions:
1407 // def SomeMethod()
1408 // enddef
1409 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001410 // enddef
1411 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001412 // def <Tval> someMethod()
1413 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001414 else if (checkforcmd(&p, "def", 3))
1415 {
1416 exarg_T ea;
1417 garray_T lines_to_free;
1418
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001419 // TODO: error for "public static def Func()"?
1420
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001421 CLEAR_FIELD(ea);
1422 ea.cmd = line;
1423 ea.arg = p;
1424 ea.cmdidx = CMD_def;
1425 ea.getline = eap->getline;
1426 ea.cookie = eap->cookie;
1427
1428 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001429 int class_flags;
1430 if (is_class)
1431 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
1432 else
1433 class_flags = CF_INTERFACE;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001434 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001435 class_flags);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001436 ga_clear_strings(&lines_to_free);
1437
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001438 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001439 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001440 char_u *name = uf->uf_name;
1441 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001442
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001443 if (is_new && !is_valid_constructor(uf, is_abstract,
1444 has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001445 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001446 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001447 break;
1448 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001449
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001450 garray_T *fgap = has_static || is_new
1451 ? &classfunctions : &objmethods;
Bram Moolenaar58b40092023-01-11 15:59:05 +00001452 // Check the name isn't used already.
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001453 if (is_duplicate_method(fgap, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001454 {
1455 success = FALSE;
1456 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001457 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001458 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001459
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001460 if (ga_grow(fgap, 1) == OK)
1461 {
1462 if (is_new)
1463 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001464
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001465 if (abstract_method)
1466 uf->uf_flags |= FC_ABSTRACT;
1467
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001468 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1469 ++fgap->ga_len;
1470 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001471 }
1472 }
1473
1474 // class members
1475 else if (has_static)
1476 {
1477 // class members (public, read access, private):
1478 // "static _varname"
1479 // "static varname"
1480 // "public static varname"
1481 char_u *varname = p;
1482 char_u *varname_end = NULL;
1483 type_T *type = NULL;
1484 char_u *init_expr = NULL;
1485 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001486 &varname_end, &type_list, &type,
1487 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001488 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001489 if (is_duplicate_member(&classmembers, varname, varname_end))
1490 {
1491 vim_free(init_expr);
1492 break;
1493 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001494 if (add_member(&classmembers, varname, varname_end,
1495 has_public, type, init_expr) == FAIL)
1496 {
1497 vim_free(init_expr);
1498 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001499 }
1500 }
1501
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001502 else
1503 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001504 if (is_class)
1505 semsg(_(e_not_valid_command_in_class_str), line);
1506 else
1507 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001508 break;
1509 }
1510 }
1511 vim_free(theline);
1512
Bram Moolenaar83677162023-01-08 19:54:10 +00001513 class_T *extends_cl = NULL; // class from "extends" argument
1514
1515 /*
1516 * Check a few things before defining the class.
1517 */
1518
1519 // Check the "extends" class is valid.
1520 if (success && extends != NULL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001521 success = validate_extends_class(extends, &extends_cl);
Bram Moolenaar83677162023-01-08 19:54:10 +00001522 VIM_CLEAR(extends);
1523
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001524 // Check the new class members and object members are not duplicates of the
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001525 // members in the extended class lineage.
1526 if (success && extends_cl != NULL)
1527 success = validate_extends_members(&classmembers, &objmembers,
1528 extends_cl);
1529
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001530 // When extending an abstract class, make sure all the abstract methods in
1531 // the parent class are implemented. If the current class is an abstract
1532 // class, then there is no need for this check.
1533 if (success && !is_abstract && extends_cl != NULL
1534 && (extends_cl->class_flags & CLASS_ABSTRACT))
1535 success = validate_extends_methods(&classfunctions, &objmethods,
1536 extends_cl);
1537
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001538 class_T **intf_classes = NULL;
1539
Bram Moolenaar83677162023-01-08 19:54:10 +00001540 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001541 if (success && ga_impl.ga_len > 0)
1542 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001543 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1544
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001545 success = validate_implements_classes(&ga_impl, intf_classes,
1546 &classfunctions, &classmembers,
1547 &objmethods, &objmembers);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001548 }
1549
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001550 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001551 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001552 success = check_func_arg_names(&classfunctions, &objmethods,
1553 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001554
Bram Moolenaareb533502022-12-14 15:06:11 +00001555 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001556 if (success)
1557 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001558 // "endclass" encountered without failures: Create the class.
1559
Bram Moolenaareb533502022-12-14 15:06:11 +00001560 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001561 if (cl == NULL)
1562 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001563 if (!is_class)
1564 cl->class_flags = CLASS_INTERFACE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001565 else if (is_abstract)
1566 cl->class_flags = CLASS_ABSTRACT;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001567
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001568 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001569 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001570 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001571 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001572
Bram Moolenaard0200c82023-01-28 15:19:40 +00001573 if (extends_cl != NULL)
1574 {
1575 cl->class_extends = extends_cl;
1576 extends_cl->class_flags |= CLASS_EXTENDED;
1577 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001578
Bram Moolenaard505d172022-12-18 21:42:55 +00001579 // Add class and object members to "cl".
1580 if (add_members_to_class(&classmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001581 extends_cl == NULL ? NULL
1582 : extends_cl->class_class_members,
1583 extends_cl == NULL ? 0
1584 : extends_cl->class_class_member_count,
1585 &cl->class_class_members,
1586 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001587 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001588 extends_cl == NULL ? NULL
1589 : extends_cl->class_obj_members,
1590 extends_cl == NULL ? 0
1591 : extends_cl->class_obj_member_count,
1592 &cl->class_obj_members,
1593 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001594 goto cleanup;
1595
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001596 if (ga_impl.ga_len > 0)
1597 {
1598 // Move the "implements" names into the class.
1599 cl->class_interface_count = ga_impl.ga_len;
1600 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1601 if (cl->class_interfaces == NULL)
1602 goto cleanup;
1603 for (int i = 0; i < ga_impl.ga_len; ++i)
1604 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1605 VIM_CLEAR(ga_impl.ga_data);
1606 ga_impl.ga_len = 0;
1607
Bram Moolenaard0200c82023-01-28 15:19:40 +00001608 cl->class_interfaces_cl = intf_classes;
1609 intf_classes = NULL;
1610 }
1611
1612 if (cl->class_interface_count > 0 || extends_cl != NULL)
1613 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001614 // Add a method and member lookup table to each of the interface
1615 // classes.
1616 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1617 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001618 }
1619
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001620 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001621 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001622 add_class_members(cl, eap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001623
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001624 int have_new = FALSE;
1625 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001626 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001627 {
1628 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1629 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001630 {
1631 have_new = TRUE;
1632 break;
1633 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001634 }
1635
1636 if (have_new)
1637 // The return type of new() is an object of class "cl"
1638 class_func->uf_ret_type->tt_class = cl;
1639 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001640 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001641 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001642
Bram Moolenaar58b40092023-01-11 15:59:05 +00001643 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001644 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1645 &objmethods) == FAIL)
1646 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001647
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001648 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001649 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001650 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001651 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001652 cl->class_type_list = type_list;
1653
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001654 class_created(cl);
1655
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001656 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001657 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001658
1659 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001660 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02001661 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001662 typval_T tv;
1663 tv.v_type = VAR_CLASS;
1664 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001665 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001666 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001667 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001668 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001669 return;
1670 }
1671
1672cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00001673 if (cl != NULL)
1674 {
1675 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001676 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001677 if (cl->class_interfaces != NULL)
1678 {
1679 for (int i = 0; i < cl->class_interface_count; ++i)
1680 vim_free(cl->class_interfaces[i]);
1681 vim_free(cl->class_interfaces);
1682 }
1683 if (cl->class_interfaces_cl != NULL)
1684 {
1685 for (int i = 0; i < cl->class_interface_count; ++i)
1686 class_unref(cl->class_interfaces_cl[i]);
1687 vim_free(cl->class_interfaces_cl);
1688 }
Bram Moolenaareb533502022-12-14 15:06:11 +00001689 vim_free(cl->class_obj_members);
1690 vim_free(cl->class_obj_methods);
1691 vim_free(cl);
1692 }
1693
Bram Moolenaar83677162023-01-08 19:54:10 +00001694 vim_free(extends);
1695 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001696
1697 if (intf_classes != NULL)
1698 {
1699 for (int i = 0; i < ga_impl.ga_len; ++i)
1700 class_unref(intf_classes[i]);
1701 vim_free(intf_classes);
1702 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001703 ga_clear_strings(&ga_impl);
1704
Bram Moolenaard505d172022-12-18 21:42:55 +00001705 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001706 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001707 garray_T *gap = round == 1 ? &classmembers : &objmembers;
1708 if (gap->ga_len == 0 || gap->ga_data == NULL)
1709 continue;
1710
1711 for (int i = 0; i < gap->ga_len; ++i)
1712 {
1713 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
1714 vim_free(m->ocm_name);
1715 vim_free(m->ocm_init);
1716 }
1717 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001718 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001719
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001720 for (int i = 0; i < objmethods.ga_len; ++i)
1721 {
1722 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
1723 func_clear_free(uf, FALSE);
1724 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001725 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001726
1727 for (int i = 0; i < classfunctions.ga_len; ++i)
1728 {
1729 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
1730 func_clear_free(uf, FALSE);
1731 }
1732 ga_clear(&classfunctions);
1733
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001734 clear_type_list(&type_list);
1735}
1736
1737/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001738 * Find member "name" in class "cl", set "member_idx" to the member index and
1739 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001740 * When "is_object" is TRUE, then look for object members. Otherwise look for
1741 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001742 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02001743 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001744 */
1745 type_T *
1746class_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001747 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001748 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001749 char_u *name,
1750 char_u *name_end,
1751 int *member_idx,
Ernie Rael456ae552023-09-01 18:54:54 +02001752 ocmember_T **p_m)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001753{
1754 *member_idx = -1; // not found (yet)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001755 size_t len = name_end - name;
1756 int member_count = is_object ? cl->class_obj_member_count
1757 : cl->class_class_member_count;
1758 ocmember_T *members = is_object ? cl->class_obj_members
1759 : cl->class_class_members;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001760
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001761 for (int i = 0; i < member_count; ++i)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001762 {
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001763 ocmember_T *m = members + i;
Bram Moolenaard505d172022-12-18 21:42:55 +00001764 if (STRNCMP(m->ocm_name, name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001765 {
1766 *member_idx = i;
Ernie Rael456ae552023-09-01 18:54:54 +02001767 if (p_m != NULL)
1768 *p_m = m;
Bram Moolenaard505d172022-12-18 21:42:55 +00001769 return m->ocm_type;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001770 }
1771 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001772
1773 semsg(_(e_unknown_variable_str), name);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001774 return &t_any;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001775}
1776
1777/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001778 * Handle ":enum" up to ":endenum".
1779 */
1780 void
1781ex_enum(exarg_T *eap UNUSED)
1782{
1783 // TODO
1784}
1785
1786/*
1787 * Handle ":type".
1788 */
1789 void
1790ex_type(exarg_T *eap UNUSED)
1791{
1792 // TODO
1793}
1794
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001795/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001796 * Returns OK if a member variable named "name" is present in the class "cl".
1797 * Otherwise returns FAIL. If found, the member variable typval is set in
1798 * "rettv". If "is_object" is TRUE, then the object member variable table is
1799 * searched. Otherwise the class member variable table is searched.
1800 */
1801 static int
1802get_member_tv(
1803 class_T *cl,
1804 int is_object,
1805 char_u *name,
1806 size_t namelen,
1807 typval_T *rettv)
1808{
1809 int member_count = is_object ? cl->class_obj_member_count
1810 : cl->class_class_member_count;
1811 ocmember_T *members = is_object ? cl->class_obj_members
1812 : cl->class_class_members;
1813
1814 for (int i = 0; i < member_count; ++i)
1815 {
1816 ocmember_T *m = &members[i];
1817 if (STRNCMP(name, m->ocm_name, namelen) == 0
1818 && m->ocm_name[namelen] == NUL)
1819 {
1820 if (*name == '_')
1821 {
1822 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
1823 return FAIL;
1824 }
1825
1826 // The object only contains a pointer to the class, the member
1827 // values array follows right after that.
1828 object_T *obj = rettv->vval.v_object;
1829 if (is_object)
1830 {
1831 typval_T *tv = (typval_T *)(obj + 1) + i;
1832 copy_tv(tv, rettv);
1833 }
1834 else
1835 copy_tv(&cl->class_members_tv[i], rettv);
1836
1837 object_unref(obj);
1838
1839 return OK;
1840 }
1841 }
1842
1843 return FAIL;
1844}
1845
1846/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001847 * Evaluate what comes after a class:
1848 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001849 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001850 * - class constructor: SomeClass.new()
1851 * - object member: someObject.varname
1852 * - object method: someObject.SomeMethod()
1853 *
1854 * "*arg" points to the '.'.
1855 * "*arg" is advanced to after the member name or method call.
1856 *
1857 * Returns FAIL or OK.
1858 */
1859 int
1860class_object_index(
1861 char_u **arg,
1862 typval_T *rettv,
1863 evalarg_T *evalarg,
1864 int verbose UNUSED) // give error messages
1865{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001866 if (VIM_ISWHITE((*arg)[1]))
1867 {
1868 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
1869 return FAIL;
1870 }
1871
1872 ++*arg;
1873 char_u *name = *arg;
1874 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
1875 if (name_end == name)
1876 return FAIL;
1877 size_t len = name_end - name;
1878
Bram Moolenaar552bdca2023-02-17 21:08:50 +00001879 class_T *cl;
1880 if (rettv->v_type == VAR_CLASS)
1881 cl = rettv->vval.v_class;
1882 else // VAR_OBJECT
1883 {
1884 if (rettv->vval.v_object == NULL)
1885 {
1886 emsg(_(e_using_null_object));
1887 return FAIL;
1888 }
1889 cl = rettv->vval.v_object->obj_class;
1890 }
1891
Bram Moolenaard13dd302023-03-11 20:56:35 +00001892 if (cl == NULL)
1893 {
1894 emsg(_(e_incomplete_type));
1895 return FAIL;
1896 }
1897
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001898 if (*name_end == '(')
1899 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001900 int on_class = rettv->v_type == VAR_CLASS;
1901 int count = on_class ? cl->class_class_function_count
1902 : cl->class_obj_method_count;
1903 for (int i = 0; i < count; ++i)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001904 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001905 ufunc_T *fp = on_class ? cl->class_class_functions[i]
1906 : cl->class_obj_methods[i];
Bram Moolenaar4ae00572022-12-09 22:49:23 +00001907 // Use a separate pointer to avoid that ASAN complains about
1908 // uf_name[] only being 4 characters.
1909 char_u *ufname = (char_u *)fp->uf_name;
1910 if (STRNCMP(name, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001911 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001912 typval_T argvars[MAX_FUNC_ARGS + 1];
1913 int argcount = 0;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001914
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001915 if (*ufname == '_')
Yegappan Lakshmanancd7293b2023-08-27 19:18:23 +02001916 {
1917 // Cannot access a private method outside of a class
1918 semsg(_(e_cannot_access_private_method_str), name);
1919 return FAIL;
1920 }
1921
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001922 char_u *argp = name_end;
1923 int ret = get_func_arguments(&argp, evalarg, 0,
1924 argvars, &argcount);
1925 if (ret == FAIL)
1926 return FAIL;
1927
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001928 funcexe_T funcexe;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001929 CLEAR_FIELD(funcexe);
1930 funcexe.fe_evaluate = TRUE;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001931 if (rettv->v_type == VAR_OBJECT)
1932 {
1933 funcexe.fe_object = rettv->vval.v_object;
1934 ++funcexe.fe_object->obj_refcount;
1935 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001936
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001937 // Clear the class or object after calling the function, in
1938 // case the refcount is one.
1939 typval_T tv_tofree = *rettv;
1940 rettv->v_type = VAR_UNKNOWN;
1941
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001942 // Call the user function. Result goes into rettv;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001943 int error = call_user_func_check(fp, argcount, argvars,
1944 rettv, &funcexe, NULL);
1945
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001946 // Clear the previous rettv and the arguments.
1947 clear_tv(&tv_tofree);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001948 for (int idx = 0; idx < argcount; ++idx)
1949 clear_tv(&argvars[idx]);
1950
1951 if (error != FCERR_NONE)
1952 {
1953 user_func_error(error, printable_func_name(fp),
1954 funcexe.fe_found_var);
1955 return FAIL;
1956 }
1957 *arg = argp;
1958 return OK;
1959 }
1960 }
1961
1962 semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name);
1963 }
1964
1965 else if (rettv->v_type == VAR_OBJECT)
1966 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001967 // Search in the object member variable table and the class member
1968 // variable table.
Yegappan Lakshmanan23c92d92023-09-09 11:33:29 +02001969 if (get_member_tv(cl, TRUE, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001970 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001971 *arg = name_end;
1972 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001973 }
1974
1975 semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
1976 }
1977
Bram Moolenaard505d172022-12-18 21:42:55 +00001978 else if (rettv->v_type == VAR_CLASS)
1979 {
1980 // class member
1981 for (int i = 0; i < cl->class_class_member_count; ++i)
1982 {
1983 ocmember_T *m = &cl->class_class_members[i];
1984 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
1985 {
1986 if (*name == '_')
1987 {
1988 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
1989 return FAIL;
1990 }
Ernie Rael18143d32023-09-04 22:30:41 +02001991 if ((cl->class_flags & CLASS_INTERFACE) != 0)
1992 {
1993 semsg(_(e_interface_static_direct_access_str),
1994 cl->class_name, m->ocm_name);
1995 return FAIL;
1996 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001997
1998 typval_T *tv = &cl->class_members_tv[i];
1999 copy_tv(tv, rettv);
2000 class_unref(cl);
2001
2002 *arg = name_end;
2003 return OK;
2004 }
2005 }
2006
2007 semsg(_(e_member_not_found_on_class_str_str), cl->class_name, name);
2008 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002009
2010 return FAIL;
2011}
2012
2013/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002014 * If "arg" points to a class or object method, return it.
2015 * Otherwise return NULL.
2016 */
2017 ufunc_T *
2018find_class_func(char_u **arg)
2019{
2020 char_u *name = *arg;
2021 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2022 if (name_end == name || *name_end != '.')
2023 return NULL;
2024
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002025 size_t len = name_end - name;
2026 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002027 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00002028 if (eval_variable(name, (int)len,
2029 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002030 return NULL;
2031 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00002032 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002033
2034 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
2035 : tv.vval.v_object->obj_class;
2036 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002037 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002038 char_u *fname = name_end + 1;
2039 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
2040 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00002041 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002042 len = fname_end - fname;
2043
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002044 int count = tv.v_type == VAR_CLASS ? cl->class_class_function_count
2045 : cl->class_obj_method_count;
2046 ufunc_T **funcs = tv.v_type == VAR_CLASS ? cl->class_class_functions
2047 : cl->class_obj_methods;
2048 for (int i = 0; i < count; ++i)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002049 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002050 ufunc_T *fp = funcs[i];
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002051 // Use a separate pointer to avoid that ASAN complains about
2052 // uf_name[] only being 4 characters.
2053 char_u *ufname = (char_u *)fp->uf_name;
2054 if (STRNCMP(fname, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002055 {
2056 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002057 return fp;
Bram Moolenaareb533502022-12-14 15:06:11 +00002058 }
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002059 }
2060
Bram Moolenaareb533502022-12-14 15:06:11 +00002061fail_after_eval:
2062 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002063 return NULL;
2064}
2065
2066/*
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002067 * If "name[len]" is a class member in cctx->ctx_ufunc->uf_class return the
2068 * index in class.class_class_members[].
2069 * If "cl_ret" is not NULL set it to the class.
2070 * Otherwise return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002071 */
2072 int
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002073class_member_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002074{
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002075 if (cctx == NULL || cctx->ctx_ufunc == NULL
2076 || cctx->ctx_ufunc->uf_class == NULL)
2077 return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002078 class_T *cl = cctx->ctx_ufunc->uf_class;
2079
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002080 for (int i = 0; i < cl->class_class_member_count; ++i)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002081 {
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002082 ocmember_T *m = &cl->class_class_members[i];
2083 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002084 {
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002085 if (cl_ret != NULL)
2086 *cl_ret = cl;
2087 return i;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002088 }
2089 }
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002090 return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002091}
2092
2093/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00002094 * Return TRUE if current context "cctx_arg" is inside class "cl".
2095 * Return FALSE if not.
2096 */
2097 int
2098inside_class(cctx_T *cctx_arg, class_T *cl)
2099{
2100 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002101 if (cctx->ctx_ufunc != NULL
2102 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002103 return TRUE;
2104 return FALSE;
2105}
2106
2107/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002108 * Make a copy of an object.
2109 */
2110 void
2111copy_object(typval_T *from, typval_T *to)
2112{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002113 if (from->vval.v_object == NULL)
2114 to->vval.v_object = NULL;
2115 else
2116 {
2117 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002118 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002119 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002120}
2121
2122/*
2123 * Free an object.
2124 */
2125 static void
2126object_clear(object_T *obj)
2127{
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002128 // Avoid a recursive call, it can happen if "obj" has a circular reference.
2129 obj->obj_refcount = INT_MAX;
2130
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002131 class_T *cl = obj->obj_class;
2132
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002133 if (!cl)
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002134 return;
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002135
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002136 // the member values are just after the object structure
2137 typval_T *tv = (typval_T *)(obj + 1);
2138 for (int i = 0; i < cl->class_obj_member_count; ++i)
2139 clear_tv(tv + i);
2140
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002141 // Remove from the list headed by "first_object".
2142 object_cleared(obj);
2143
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002144 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002145 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002146}
2147
2148/*
2149 * Unreference an object.
2150 */
2151 void
2152object_unref(object_T *obj)
2153{
2154 if (obj != NULL && --obj->obj_refcount <= 0)
2155 object_clear(obj);
2156}
2157
2158/*
2159 * Make a copy of a class.
2160 */
2161 void
2162copy_class(typval_T *from, typval_T *to)
2163{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002164 if (from->vval.v_class == NULL)
2165 to->vval.v_class = NULL;
2166 else
2167 {
2168 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002169 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002170 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002171}
2172
2173/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002174 * Free the class "cl" and its contents.
2175 */
2176 static void
2177class_free(class_T *cl)
2178{
2179 // Freeing what the class contains may recursively come back here.
2180 // Clear "class_name" first, if it is NULL the class does not need to
2181 // be freed.
2182 VIM_CLEAR(cl->class_name);
2183
2184 class_unref(cl->class_extends);
2185
2186 for (int i = 0; i < cl->class_interface_count; ++i)
2187 {
2188 vim_free(((char_u **)cl->class_interfaces)[i]);
2189 if (cl->class_interfaces_cl[i] != NULL)
2190 class_unref(cl->class_interfaces_cl[i]);
2191 }
2192 vim_free(cl->class_interfaces);
2193 vim_free(cl->class_interfaces_cl);
2194
2195 itf2class_T *next;
2196 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2197 {
2198 next = i2c->i2c_next;
2199 vim_free(i2c);
2200 }
2201
2202 for (int i = 0; i < cl->class_class_member_count; ++i)
2203 {
2204 ocmember_T *m = &cl->class_class_members[i];
2205 vim_free(m->ocm_name);
2206 vim_free(m->ocm_init);
2207 if (cl->class_members_tv != NULL)
2208 clear_tv(&cl->class_members_tv[i]);
2209 }
2210 vim_free(cl->class_class_members);
2211 vim_free(cl->class_members_tv);
2212
2213 for (int i = 0; i < cl->class_obj_member_count; ++i)
2214 {
2215 ocmember_T *m = &cl->class_obj_members[i];
2216 vim_free(m->ocm_name);
2217 vim_free(m->ocm_init);
2218 }
2219 vim_free(cl->class_obj_members);
2220
2221 for (int i = 0; i < cl->class_class_function_count; ++i)
2222 {
2223 ufunc_T *uf = cl->class_class_functions[i];
2224 func_clear_free(uf, FALSE);
2225 }
2226 vim_free(cl->class_class_functions);
2227
2228 for (int i = 0; i < cl->class_obj_method_count; ++i)
2229 {
2230 ufunc_T *uf = cl->class_obj_methods[i];
2231 func_clear_free(uf, FALSE);
2232 }
2233 vim_free(cl->class_obj_methods);
2234
2235 clear_type_list(&cl->class_type_list);
2236
2237 class_cleared(cl);
2238
2239 vim_free(cl);
2240}
2241
2242/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002243 * Unreference a class. Free it when the reference count goes down to zero.
2244 */
2245 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002246class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002247{
Bram Moolenaard505d172022-12-18 21:42:55 +00002248 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002249 class_free(cl);
2250}
2251
2252/*
2253 * Go through the list of all classes and free items without "copyID".
2254 */
2255 int
2256class_free_nonref(int copyID)
2257{
2258 int did_free = FALSE;
2259
2260 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002261 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002262 next_nonref_class = cl->class_next_used;
2263 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002264 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002265 // Free the class and items it contains.
2266 class_free(cl);
2267 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002268 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002269 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002270
2271 next_nonref_class = NULL;
2272 return did_free;
2273}
2274
2275 int
2276set_ref_in_classes(int copyID)
2277{
2278 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
2279 set_ref_in_item_class(cl, copyID, NULL, NULL);
2280
2281 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002282}
2283
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002284static object_T *first_object = NULL;
2285
2286/*
2287 * Call this function when an object has been created. It will be added to the
2288 * list headed by "first_object".
2289 */
2290 void
2291object_created(object_T *obj)
2292{
2293 if (first_object != NULL)
2294 {
2295 obj->obj_next_used = first_object;
2296 first_object->obj_prev_used = obj;
2297 }
2298 first_object = obj;
2299}
2300
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002301static object_T *next_nonref_obj = NULL;
2302
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002303/*
2304 * Call this function when an object has been cleared and is about to be freed.
2305 * It is removed from the list headed by "first_object".
2306 */
2307 void
2308object_cleared(object_T *obj)
2309{
2310 if (obj->obj_next_used != NULL)
2311 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
2312 if (obj->obj_prev_used != NULL)
2313 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
2314 else if (first_object == obj)
2315 first_object = obj->obj_next_used;
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002316
2317 // update the next object to check if needed
2318 if (obj == next_nonref_obj)
2319 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002320}
2321
2322/*
2323 * Go through the list of all objects and free items without "copyID".
2324 */
2325 int
2326object_free_nonref(int copyID)
2327{
2328 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002329
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002330 for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002331 {
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002332 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002333 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
2334 {
2335 // Free the object and items it contains.
2336 object_clear(obj);
2337 did_free = TRUE;
2338 }
2339 }
2340
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002341 next_nonref_obj = NULL;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002342 return did_free;
2343}
2344
LemonBoyafe04662023-08-23 21:08:11 +02002345/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002346 * Return TRUE when the class "cl", its base class or one of the implemented
2347 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02002348 */
2349 int
2350class_instance_of(class_T *cl, class_T *other_cl)
2351{
2352 if (cl == other_cl)
2353 return TRUE;
2354
2355 // Recursively check the base classes.
2356 for (; cl != NULL; cl = cl->class_extends)
2357 {
2358 if (cl == other_cl)
2359 return TRUE;
2360 // Check the implemented interfaces.
2361 for (int i = cl->class_interface_count - 1; i >= 0; --i)
2362 if (cl->class_interfaces_cl[i] == other_cl)
2363 return TRUE;
2364 }
2365
2366 return FALSE;
2367}
2368
2369/*
2370 * "instanceof(object, classinfo)" function
2371 */
2372 void
2373f_instanceof(typval_T *argvars, typval_T *rettv)
2374{
2375 typval_T *object_tv = &argvars[0];
2376 typval_T *classinfo_tv = &argvars[1];
2377 listitem_T *li;
2378
2379 rettv->vval.v_number = VVAL_FALSE;
2380
2381 if (check_for_object_arg(argvars, 0) == FAIL
2382 || check_for_class_or_list_arg(argvars, 1) == FAIL)
2383 return;
2384
2385 if (classinfo_tv->v_type == VAR_LIST)
2386 {
2387 FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
2388 {
2389 if (li->li_tv.v_type != VAR_CLASS)
2390 {
2391 emsg(_(e_class_required));
2392 return;
2393 }
2394
2395 if (class_instance_of(object_tv->vval.v_object->obj_class,
2396 li->li_tv.vval.v_class) == TRUE)
2397 {
2398 rettv->vval.v_number = VVAL_TRUE;
2399 return;
2400 }
2401 }
2402 }
2403 else if (classinfo_tv->v_type == VAR_CLASS)
2404 {
2405 rettv->vval.v_number = class_instance_of(object_tv->vval.v_object->obj_class,
2406 classinfo_tv->vval.v_class);
2407 }
2408}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002409
2410#endif // FEAT_EVAL