blob: a65173d61dd50bd153529dd637fa1779abca71b4 [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
Ernie Raelcf138d42023-09-06 20:45:03 +0200240 itf2class_T *i2c = NULL;
241 int searching = TRUE;
242 for (class_T *super = cl; super != NULL && searching;
243 super = super->class_extends)
244 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
245 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
246 {
247 searching = FALSE;
248 break;
249 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000250 if (i2c == NULL)
251 {
252 siemsg("class %s not found on interface %s",
253 cl->class_name, itf->class_name);
254 return 0;
255 }
Ernie Rael18143d32023-09-04 22:30:41 +0200256 if (is_static)
257 {
258 // TODO: Need a table for fast lookup?
259 char_u *name = itf->class_class_members[idx].ocm_name;
260 for (int i = 0; i < i2c->i2c_class->class_class_member_count; ++i)
261 {
262 ocmember_T *m = &i2c->i2c_class->class_class_members[i];
263 if (STRCMP(name, m->ocm_name) == 0)
264 {
265 return i;
266 }
267 }
268 siemsg("class %s, interface %s, static %s not found",
269 cl->class_name, itf->class_name, name);
270 return 0;
271 }
272 else
273 {
274 // A table follows the i2c for the class
275 int *table = (int *)(i2c + 1);
276 return table[idx];
277 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000278}
279
280/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200281 * Check whether a class named "extends_name" is present. If the class is
282 * valid, then "extends_clp" is set with the class pointer.
283 * Returns TRUE if the class name "extends_names" is a valid class.
284 */
285 static int
286validate_extends_class(char_u *extends_name, class_T **extends_clp)
287{
288 typval_T tv;
289 int success = FALSE;
290
291 tv.v_type = VAR_UNKNOWN;
292 if (eval_variable_import(extends_name, &tv) == FAIL)
293 {
294 semsg(_(e_class_name_not_found_str), extends_name);
295 return success;
296 }
297 else
298 {
299 if (tv.v_type != VAR_CLASS
300 || tv.vval.v_class == NULL
301 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
302 semsg(_(e_cannot_extend_str), extends_name);
303 else
304 {
305 class_T *extends_cl = tv.vval.v_class;
306 ++extends_cl->class_refcount;
307 *extends_clp = extends_cl;
308 success = TRUE;
309 }
310 clear_tv(&tv);
311 }
312
313 return success;
314}
315
316/*
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200317 * Check whether a class/object member variable in "classmembers_gap" /
318 * "objmembers_gap" is a duplicate of a member in any of the extended parent
319 * class lineage. Returns TRUE if there are no duplicates.
320 */
321 static int
322validate_extends_members(
323 garray_T *classmembers_gap,
324 garray_T *objmembers_gap,
325 class_T *extends_cl)
326{
327 for (int loop = 1; loop <= 2; ++loop)
328 {
329 // loop == 1: check class members
330 // loop == 2: check object members
331 int member_count = loop == 1 ? classmembers_gap->ga_len
332 : objmembers_gap->ga_len;
333 if (member_count == 0)
334 continue;
335 ocmember_T *members = (ocmember_T *)(loop == 1
336 ? classmembers_gap->ga_data
337 : objmembers_gap->ga_data);
338
339 // Validate each member variable
340 for (int c_i = 0; c_i < member_count; c_i++)
341 {
342 class_T *p_cl = extends_cl;
343 ocmember_T *c_m = members + c_i;
344 char_u *pstr = (*c_m->ocm_name == '_')
345 ? c_m->ocm_name + 1 : c_m->ocm_name;
346
347 // Check in all the parent classes in the lineage
348 while (p_cl != NULL)
349 {
350 int p_member_count = loop == 1
351 ? p_cl->class_class_member_count
352 : p_cl->class_obj_member_count;
353 if (p_member_count == 0)
354 continue;
355 ocmember_T *p_members = (loop == 1
356 ? p_cl->class_class_members
357 : p_cl->class_obj_members);
358
359 // Compare against all the members in the parent class
360 for (int p_i = 0; p_i < p_member_count; p_i++)
361 {
362 ocmember_T *p_m = p_members + p_i;
363 char_u *qstr = (*p_m->ocm_name == '_')
364 ? p_m->ocm_name + 1 : p_m->ocm_name;
365 if (STRCMP(pstr, qstr) == 0)
366 {
367 semsg(_(e_duplicate_member_str), c_m->ocm_name);
368 return FALSE;
369 }
370 }
371
372 p_cl = p_cl->class_extends;
373 }
374 }
375 }
376
377 return TRUE;
378}
379
380/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200381 * Check the members of the interface class "ifcl" match the class members
382 * ("classmembers_gap") and object members ("objmembers_gap") of a class.
383 * Returns TRUE if the class and object member names are valid.
384 */
385 static int
386validate_interface_members(
387 char_u *intf_class_name,
388 class_T *ifcl,
389 garray_T *classmembers_gap,
390 garray_T *objmembers_gap)
391{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200392 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200393 {
394 // loop == 1: check class members
395 // loop == 2: check object members
396 int if_count = loop == 1 ? ifcl->class_class_member_count
397 : ifcl->class_obj_member_count;
398 if (if_count == 0)
399 continue;
400 ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
401 : ifcl->class_obj_members;
402 ocmember_T *cl_ms = (ocmember_T *)(loop == 1
403 ? classmembers_gap->ga_data
404 : objmembers_gap->ga_data);
405 int cl_count = loop == 1 ? classmembers_gap->ga_len
406 : objmembers_gap->ga_len;
407 for (int if_i = 0; if_i < if_count; ++if_i)
408 {
409 int cl_i;
410 for (cl_i = 0; cl_i < cl_count; ++cl_i)
411 {
412 ocmember_T *m = &cl_ms[cl_i];
413 where_T where = WHERE_INIT;
414
415 if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) != 0)
416 continue;
417
418 // Ensure the type is matching.
419 where.wt_func_name = (char *)m->ocm_name;
420 where.wt_kind = WT_MEMBER;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200421 if (check_type(if_ms[if_i].ocm_type, m->ocm_type, TRUE,
422 where) == FAIL)
423 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200424
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200425 if (if_ms[if_i].ocm_access != m->ocm_access)
426 {
427 semsg(_(e_member_str_of_interface_str_has_different_access),
428 if_ms[if_i].ocm_name, intf_class_name);
429 return FALSE;
430 }
431
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200432 break;
433 }
434 if (cl_i == cl_count)
435 {
436 semsg(_(e_member_str_of_interface_str_not_implemented),
437 if_ms[if_i].ocm_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200438 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200439 }
440 }
441 }
442
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200443 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200444}
445
446/*
447 * Check the functions/methods of the interface class "ifcl" match the class
448 * methods ("classfunctions_gap") and object functions ("objmemthods_gap") of a
449 * class.
450 * Returns TRUE if the class and object member names are valid.
451 */
452 static int
453validate_interface_methods(
454 char_u *intf_class_name,
455 class_T *ifcl,
456 garray_T *classfunctions_gap,
457 garray_T *objmethods_gap)
458{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200459 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200460 {
461 // loop == 1: check class functions
462 // loop == 2: check object methods
463 int if_count = loop == 1 ? ifcl->class_class_function_count
464 : ifcl->class_obj_method_count;
465 if (if_count == 0)
466 continue;
467 ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
468 : ifcl->class_obj_methods;
469 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
470 ? classfunctions_gap->ga_data
471 : objmethods_gap->ga_data);
472 int cl_count = loop == 1 ? classfunctions_gap->ga_len
473 : objmethods_gap->ga_len;
474 for (int if_i = 0; if_i < if_count; ++if_i)
475 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200476 char_u *if_name = if_fp[if_i]->uf_name;
477 int cl_i;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200478 for (cl_i = 0; cl_i < cl_count; ++cl_i)
479 {
480 char_u *cl_name = cl_fp[cl_i]->uf_name;
481 if (STRCMP(if_name, cl_name) == 0)
482 {
483 where_T where = WHERE_INIT;
484
485 // Ensure the type is matching.
486 where.wt_func_name = (char *)if_name;
487 where.wt_kind = WT_METHOD;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200488 if (check_type(if_fp[if_i]->uf_func_type,
489 cl_fp[cl_i]->uf_func_type, TRUE, where) == FAIL)
490 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200491 break;
492 }
493 }
494 if (cl_i == cl_count)
495 {
496 semsg(_(e_function_str_of_interface_str_not_implemented),
497 if_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200498 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200499 }
500 }
501 }
502
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200503 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200504}
505
506/*
507 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200508 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200509 * object methods and object members in the new class are in
510 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
511 * "objmembers_gap" respectively.
512 */
513 static int
514validate_implements_classes(
515 garray_T *impl_gap,
516 class_T **intf_classes,
517 garray_T *classfunctions_gap,
518 garray_T *classmembers_gap,
519 garray_T *objmethods_gap,
520 garray_T *objmembers_gap)
521{
522 int success = TRUE;
523
524 for (int i = 0; i < impl_gap->ga_len && success; ++i)
525 {
526 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
527 typval_T tv;
528 tv.v_type = VAR_UNKNOWN;
529 if (eval_variable_import(impl, &tv) == FAIL)
530 {
531 semsg(_(e_interface_name_not_found_str), impl);
532 success = FALSE;
533 break;
534 }
535
536 if (tv.v_type != VAR_CLASS
537 || tv.vval.v_class == NULL
538 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
539 {
540 semsg(_(e_not_valid_interface_str), impl);
541 success = FALSE;
542 clear_tv(&tv);
543 break;
544 }
545
546 class_T *ifcl = tv.vval.v_class;
547 intf_classes[i] = ifcl;
548 ++ifcl->class_refcount;
549
550 // check the members of the interface match the members of the class
551 success = validate_interface_members(impl, ifcl, classmembers_gap,
552 objmembers_gap);
553
554 // check the functions/methods of the interface match the
555 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200556 if (success)
557 success = validate_interface_methods(impl, ifcl,
558 classfunctions_gap, objmethods_gap);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200559 clear_tv(&tv);
560 }
561
562 return success;
563}
564
565/*
566 * Check no function argument name is used as a class member.
567 * (Object members are always accessed with "this." prefix, so no need
568 * to check them.)
569 */
570 static int
571check_func_arg_names(
572 garray_T *classfunctions_gap,
573 garray_T *objmethods_gap,
574 garray_T *classmembers_gap)
575{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200576 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200577 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200578 {
579 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
580
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200581 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200582 {
583 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
584
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200585 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200586 {
587 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
588 garray_T *mgap = classmembers_gap;
589
590 // Check all the class member names
591 for (int mi = 0; mi < mgap->ga_len; ++mi)
592 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200593 char_u *mname =
594 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200595 if (STRCMP(aname, mname) == 0)
596 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200597 if (uf->uf_script_ctx.sc_sid > 0)
598 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
599
600 semsg(_(e_argument_already_declared_in_class_str),
601 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200602
603 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200604 }
605 }
606 }
607 }
608 }
609
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200610 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200611}
612
613/*
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200614 * Returns TRUE if the member "varname" is already defined.
615 */
616 static int
617is_duplicate_member(garray_T *mgap, char_u *varname, char_u *varname_end)
618{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200619 char_u *name = vim_strnsave(varname, varname_end - varname);
620 char_u *pstr = (*name == '_') ? name + 1 : name;
621 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200622
623 for (int i = 0; i < mgap->ga_len; ++i)
624 {
625 ocmember_T *m = ((ocmember_T *)mgap->ga_data) + i;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200626 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1 : m->ocm_name;
627 if (STRCMP(pstr, qstr) == 0)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200628 {
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200629 semsg(_(e_duplicate_member_str), name);
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200630 dup = TRUE;
631 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200632 }
633 }
634
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200635 vim_free(name);
636 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200637}
638
639/*
640 * Returns TRUE if the method "name" is already defined.
641 */
642 static int
643is_duplicate_method(garray_T *fgap, char_u *name)
644{
645 char_u *pstr = (*name == '_') ? name + 1 : name;
646
647 for (int i = 0; i < fgap->ga_len; ++i)
648 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200649 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
650 char_u *qstr = *n == '_' ? n + 1 : n;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200651 if (STRCMP(pstr, qstr) == 0)
652 {
653 semsg(_(e_duplicate_function_str), name);
654 return TRUE;
655 }
656 }
657
658 return FALSE;
659}
660
661/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200662 * Returns TRUE if the constructor is valid.
663 */
664 static int
665is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
666{
667 // Constructors are not allowed in abstract classes.
668 if (is_abstract)
669 {
670 emsg(_(e_cannot_define_new_function_in_abstract_class));
671 return FALSE;
672 }
673 // A constructor is always static, no need to define it so.
674 if (has_static)
675 {
676 emsg(_(e_cannot_define_new_function_as_static));
677 return FALSE;
678 }
679 // A return type should not be specified for the new()
680 // constructor method.
681 if (uf->uf_ret_type->tt_type != VAR_VOID)
682 {
683 emsg(_(e_cannot_use_a_return_type_with_new));
684 return FALSE;
685 }
686 return TRUE;
687}
688
689/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200690 * Update the interface class lookup table for the member index on the
691 * interface to the member index in the class implementing the interface.
692 * And a lookup table for the object method index on the interface
693 * to the object method index in the class implementing the interface.
694 * This is also used for updating the lookup table for the extended class
695 * hierarchy.
696 */
697 static int
698update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200699 class_T *ifcl,
700 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200701 garray_T *objmethods,
702 int pobj_method_offset,
703 int is_interface)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200704{
705 if (ifcl == NULL)
706 return OK;
707
708 // Table for members.
709 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200710 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200711 if (if2cl == NULL)
712 return FAIL;
713 if2cl->i2c_next = ifcl->class_itf2class;
714 ifcl->class_itf2class = if2cl;
715 if2cl->i2c_class = cl;
716 if2cl->i2c_is_method = FALSE;
717
718 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
719 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
720 {
721 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200722 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200723 {
724 int *table = (int *)(if2cl + 1);
725 table[if_i] = cl_i;
726 break;
727 }
728 }
729
730 // Table for methods.
731 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200732 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200733 if (if2cl == NULL)
734 return FAIL;
735 if2cl->i2c_next = ifcl->class_itf2class;
736 ifcl->class_itf2class = if2cl;
737 if2cl->i2c_class = cl;
738 if2cl->i2c_is_method = TRUE;
739
740 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
741 {
742 int done = FALSE;
743 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
744 {
745 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200746 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200747 {
748 int *table = (int *)(if2cl + 1);
749 table[if_i] = cl_i;
750 done = TRUE;
751 break;
752 }
753 }
754
755 // extended class object method is not overridden by the child class.
756 // Keep the method declared in one of the parent classes in the
757 // lineage.
758 if (!done && !is_interface)
759 {
760 // If "ifcl" is not the immediate parent of "cl", then search in
761 // the intermediate parent classes.
762 if (cl->class_extends != ifcl)
763 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200764 class_T *parent = cl->class_extends;
765 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200766
767 while (!done && parent != NULL && parent != ifcl)
768 {
769
770 for (int cl_i = 0;
771 cl_i < parent->class_obj_method_count_child; ++cl_i)
772 {
773 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
774 parent->class_obj_methods[cl_i]->uf_name)
775 == 0)
776 {
777 int *table = (int *)(if2cl + 1);
778 table[if_i] = method_offset + cl_i;
779 done = TRUE;
780 break;
781 }
782 }
783 method_offset += parent->class_obj_method_count_child;
784 parent = parent->class_extends;
785 }
786 }
787
788 if (!done)
789 {
790 int *table = (int *)(if2cl + 1);
791 table[if_i] = pobj_method_offset + if_i;
792 }
793 }
794 }
795
796 return OK;
797}
798
799/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200800 * Update the member and object method lookup tables for a new class in the
801 * interface class.
802 * For each interface add a lookup table for the member index on the interface
803 * to the member index in the new class. And a lookup table for the object
804 * method index on the interface to the object method index in the new class.
805 */
806 static int
807add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
808{
809 for (int i = 0; i < cl->class_interface_count; ++i)
810 {
811 class_T *ifcl = cl->class_interfaces_cl[i];
812
813 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
814 0, TRUE) == FAIL)
815 return FAIL;
816 }
817
818 // Update the lookup table for the extended class, if nay
819 if (extends_cl != NULL)
820 {
821 class_T *pclass = extends_cl;
822 int pobj_method_offset = objmethods_gap->ga_len;
823
824 // Update the entire lineage of extended classes.
825 while (pclass != NULL)
826 {
827 if (update_member_method_lookup_table(pclass, cl,
828 objmethods_gap, pobj_method_offset, FALSE) == FAIL)
829 return FAIL;
830
831 pobj_method_offset += pclass->class_obj_method_count_child;
832 pclass = pclass->class_extends;
833 }
834 }
835
836 return OK;
837}
838
839/*
840 * Add class members to a new class. Allocate a typval for each class member
841 * and initialize it.
842 */
843 static void
844add_class_members(class_T *cl, exarg_T *eap)
845{
846 // Allocate a typval for each class member and initialize it.
847 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
848 cl->class_class_member_count);
849 if (cl->class_members_tv == NULL)
850 return;
851
852 for (int i = 0; i < cl->class_class_member_count; ++i)
853 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200854 ocmember_T *m = &cl->class_class_members[i];
855 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200856 if (m->ocm_init != NULL)
857 {
858 typval_T *etv = eval_expr(m->ocm_init, eap);
859 if (etv != NULL)
860 {
861 *tv = *etv;
862 vim_free(etv);
863 }
864 }
865 else
866 {
867 // TODO: proper default value
868 tv->v_type = m->ocm_type->tt_type;
869 tv->vval.v_string = NULL;
870 }
871 }
872}
873
874/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +0200875 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200876 */
877 static void
878add_default_constructor(
879 class_T *cl,
880 garray_T *classfunctions_gap,
881 garray_T *type_list_gap)
882{
883 garray_T fga;
884
885 ga_init2(&fga, 1, 1000);
886 ga_concat(&fga, (char_u *)"new(");
887 for (int i = 0; i < cl->class_obj_member_count; ++i)
888 {
889 if (i > 0)
890 ga_concat(&fga, (char_u *)", ");
891 ga_concat(&fga, (char_u *)"this.");
892 ocmember_T *m = cl->class_obj_members + i;
893 ga_concat(&fga, (char_u *)m->ocm_name);
894 ga_concat(&fga, (char_u *)" = v:none");
895 }
896 ga_concat(&fga, (char_u *)")\nenddef\n");
897 ga_append(&fga, NUL);
898
899 exarg_T fea;
900 CLEAR_FIELD(fea);
901 fea.cmdidx = CMD_def;
902 fea.cmd = fea.arg = fga.ga_data;
903
904 garray_T lines_to_free;
905 ga_init2(&lines_to_free, sizeof(char_u *), 50);
906
907 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS);
908
909 ga_clear_strings(&lines_to_free);
910 vim_free(fga.ga_data);
911
912 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
913 {
914 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200915 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200916 ++classfunctions_gap->ga_len;
917
918 nf->uf_flags |= FC_NEW;
919 nf->uf_ret_type = get_type_ptr(type_list_gap);
920 if (nf->uf_ret_type != NULL)
921 {
922 nf->uf_ret_type->tt_type = VAR_OBJECT;
923 nf->uf_ret_type->tt_class = cl;
924 nf->uf_ret_type->tt_argcount = 0;
925 nf->uf_ret_type->tt_args = NULL;
926 }
927 }
928}
929
930/*
931 * Add the class functions and object methods to the new class "cl".
932 * When extending a class, add the functions and methods from the parent class
933 * also.
934 */
935 static int
936add_classfuncs_objmethods(
937 class_T *cl,
938 class_T *extends_cl,
939 garray_T *classfunctions_gap,
940 garray_T *objmethods_gap)
941{
942 // loop 1: class functions, loop 2: object methods
943 for (int loop = 1; loop <= 2; ++loop)
944 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200945 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
946 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200947 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200948 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200949 : &cl->class_obj_methods;
950
951 int parent_count = 0;
952 if (extends_cl != NULL)
953 // Include functions from the parent.
954 parent_count = loop == 1
955 ? extends_cl->class_class_function_count
956 : extends_cl->class_obj_method_count;
957
958 *fcount = parent_count + gap->ga_len;
959 if (*fcount == 0)
960 {
961 *fup = NULL;
962 continue;
963 }
964 *fup = ALLOC_MULT(ufunc_T *, *fcount);
965 if (*fup == NULL)
966 return FAIL;
967
968 if (gap->ga_len != 0)
969 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
970 vim_free(gap->ga_data);
971 if (loop == 1)
972 cl->class_class_function_count_child = gap->ga_len;
973 else
974 cl->class_obj_method_count_child = gap->ga_len;
975
976 int skipped = 0;
977 for (int i = 0; i < parent_count; ++i)
978 {
979 // Copy functions from the parent. Can't use the same
980 // function, because "uf_class" is different and compilation
981 // will have a different result.
982 // Put them after the functions in the current class, object
983 // methods may be overruled, then "super.Method()" is used to
984 // find a method from the parent.
985 // Skip "new" functions. TODO: not all of them.
986 if (loop == 1 && STRNCMP(
987 extends_cl->class_class_functions[i]->uf_name,
988 "new", 3) == 0)
989 ++skipped;
990 else
991 {
992 ufunc_T *pf = (loop == 1
993 ? extends_cl->class_class_functions
994 : extends_cl->class_obj_methods)[i];
995 (*fup)[gap->ga_len + i - skipped] = copy_function(pf);
996
997 // If the child class overrides a function from the parent
998 // the signature must be equal.
999 char_u *pname = pf->uf_name;
1000 for (int ci = 0; ci < gap->ga_len; ++ci)
1001 {
1002 ufunc_T *cf = (*fup)[ci];
1003 char_u *cname = cf->uf_name;
1004 if (STRCMP(pname, cname) == 0)
1005 {
1006 where_T where = WHERE_INIT;
1007 where.wt_func_name = (char *)pname;
1008 where.wt_kind = WT_METHOD;
1009 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1010 TRUE, where);
1011 }
1012 }
1013 }
1014 }
1015
1016 *fcount -= skipped;
1017
1018 // Set the class pointer on all the functions and object methods.
1019 for (int i = 0; i < *fcount; ++i)
1020 {
1021 ufunc_T *fp = (*fup)[i];
1022 fp->uf_class = cl;
1023 if (loop == 2)
1024 fp->uf_flags |= FC_OBJECT;
1025 }
1026 }
1027
1028 return OK;
1029}
1030
1031/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001032 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001033 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001034 */
1035 void
1036ex_class(exarg_T *eap)
1037{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001038 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1039 long start_lnum = SOURCING_LNUM;
1040 char_u *arg = eap->arg;
1041 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001042
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001043 if (is_abstract)
1044 {
1045 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1046 {
1047 semsg(_(e_invalid_argument_str), arg);
1048 return;
1049 }
1050 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001051 is_class = TRUE;
1052 }
1053
1054 if (!current_script_is_vim9()
1055 || (cmdmod.cmod_flags & CMOD_LEGACY)
1056 || !getline_equal(eap->getline, eap->cookie, getsourceline))
1057 {
1058 if (is_class)
1059 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1060 else
1061 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1062 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001063 }
1064
1065 if (!ASCII_ISUPPER(*arg))
1066 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001067 if (is_class)
1068 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1069 else
1070 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1071 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001072 return;
1073 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001074 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1075 if (!IS_WHITE_OR_NUL(*name_end))
1076 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001077 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001078 return;
1079 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001080 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001081
Bram Moolenaara86655a2023-01-12 17:06:27 +00001082 // "export class" gets used when creating the class, don't use "is_export"
1083 // for the items inside the class.
1084 int class_export = is_export;
1085 is_export = FALSE;
1086
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001087 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001088 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001089
Bram Moolenaar83677162023-01-08 19:54:10 +00001090 // Name for "extends BaseClass"
1091 char_u *extends = NULL;
1092
Bram Moolenaar94674f22023-01-06 18:42:20 +00001093 // Names for "implements SomeInterface"
1094 garray_T ga_impl;
1095 ga_init2(&ga_impl, sizeof(char_u *), 5);
1096
1097 arg = skipwhite(name_end);
1098 while (*arg != NUL && *arg != '#' && *arg != '\n')
1099 {
1100 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001101 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001102 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1103 {
1104 if (extends != NULL)
1105 {
1106 emsg(_(e_duplicate_extends));
1107 goto early_ret;
1108 }
1109 arg = skipwhite(arg + 7);
1110 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1111 if (!IS_WHITE_OR_NUL(*end))
1112 {
1113 semsg(_(e_white_space_required_after_name_str), arg);
1114 goto early_ret;
1115 }
1116 extends = vim_strnsave(arg, end - arg);
1117 if (extends == NULL)
1118 goto early_ret;
1119
1120 arg = skipwhite(end + 1);
1121 }
1122 else if (STRNCMP(arg, "implements", 10) == 0
1123 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001124 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001125 if (ga_impl.ga_len > 0)
1126 {
1127 emsg(_(e_duplicate_implements));
1128 goto early_ret;
1129 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001130 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001131
1132 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001133 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001134 char_u *impl_end = find_name_end(arg, NULL, NULL,
1135 FNE_CHECK_START);
1136 if (!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1137 {
1138 semsg(_(e_white_space_required_after_name_str), arg);
1139 goto early_ret;
1140 }
1141 char_u *iname = vim_strnsave(arg, impl_end - arg);
1142 if (iname == NULL)
1143 goto early_ret;
1144 for (int i = 0; i < ga_impl.ga_len; ++i)
1145 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1146 {
1147 semsg(_(e_duplicate_interface_after_implements_str),
1148 iname);
1149 vim_free(iname);
1150 goto early_ret;
1151 }
1152 if (ga_add_string(&ga_impl, iname) == FAIL)
1153 {
1154 vim_free(iname);
1155 goto early_ret;
1156 }
1157 if (*impl_end != ',')
1158 {
1159 arg = skipwhite(impl_end);
1160 break;
1161 }
1162 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001163 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001164 }
1165 else
1166 {
1167 semsg(_(e_trailing_characters_str), arg);
1168early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001169 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001170 ga_clear_strings(&ga_impl);
1171 return;
1172 }
1173 }
1174
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001175 garray_T type_list; // list of pointers to allocated types
1176 ga_init2(&type_list, sizeof(type_T *), 10);
1177
Bram Moolenaard505d172022-12-18 21:42:55 +00001178 // Growarray with class members declared in the class.
1179 garray_T classmembers;
1180 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1181
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001182 // Growarray with functions declared in the class.
1183 garray_T classfunctions;
1184 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001185
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001186 // Growarray with object members declared in the class.
1187 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001188 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001189
1190 // Growarray with object methods declared in the class.
1191 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001192 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001193
1194 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001195 * Go over the body of the class/interface until "endclass" or
1196 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001197 */
1198 char_u *theline = NULL;
1199 int success = FALSE;
1200 for (;;)
1201 {
1202 vim_free(theline);
1203 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
1204 if (theline == NULL)
1205 break;
1206 char_u *line = skipwhite(theline);
1207
Bram Moolenaar418b5472022-12-20 13:38:22 +00001208 // Skip empty and comment lines.
1209 if (*line == NUL)
1210 continue;
1211 if (*line == '#')
1212 {
1213 if (vim9_bad_comment(line))
1214 break;
1215 continue;
1216 }
1217
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001218 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001219 char *end_name = is_class ? "endclass" : "endinterface";
1220 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001221 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001222 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001223 semsg(_(e_command_cannot_be_shortened_str), line);
1224 else if (*p == '|' || !ends_excmd2(line, p))
1225 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001226 else
1227 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001228 break;
1229 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001230 char *wrong_name = is_class ? "endinterface" : "endclass";
1231 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1232 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001233 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001234 break;
1235 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001236
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001237 int has_public = FALSE;
1238 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001239 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001240 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001241 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001242 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001243 break;
1244 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001245 has_public = TRUE;
1246 p = skipwhite(line + 6);
1247
Bram Moolenaard505d172022-12-18 21:42:55 +00001248 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001249 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001250 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001251 break;
1252 }
1253 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001254
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001255 int has_static = FALSE;
1256 char_u *ps = p;
1257 if (checkforcmd(&p, "static", 4))
1258 {
1259 if (STRNCMP(ps, "static", 6) != 0)
1260 {
1261 semsg(_(e_command_cannot_be_shortened_str), ps);
1262 break;
1263 }
1264 has_static = TRUE;
1265 p = skipwhite(ps + 6);
1266 }
1267
Bram Moolenaard505d172022-12-18 21:42:55 +00001268 // object members (public, read access, private):
1269 // "this._varname"
1270 // "this.varname"
1271 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001272 if (STRNCMP(p, "this", 4) == 0)
1273 {
1274 if (p[4] != '.' || !eval_isnamec1(p[5]))
1275 {
1276 semsg(_(e_invalid_object_member_declaration_str), p);
1277 break;
1278 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001279 if (has_static)
1280 {
1281 emsg(_(e_static_cannot_be_followed_by_this));
1282 break;
1283 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001284 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +00001285 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001286 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001287 char_u *init_expr = NULL;
1288 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001289 &varname_end, &type_list, &type,
1290 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001291 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001292 if (is_duplicate_member(&objmembers, varname, varname_end))
1293 {
1294 vim_free(init_expr);
1295 break;
1296 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001297 if (add_member(&objmembers, varname, varname_end,
1298 has_public, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001299 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001300 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001301 break;
1302 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001303 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001304
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001305 // constructors:
1306 // def new()
1307 // enddef
1308 // def newOther()
1309 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001310 // object methods and class functions:
1311 // def SomeMethod()
1312 // enddef
1313 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001314 // enddef
1315 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001316 // def <Tval> someMethod()
1317 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001318 else if (checkforcmd(&p, "def", 3))
1319 {
1320 exarg_T ea;
1321 garray_T lines_to_free;
1322
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001323 // TODO: error for "public static def Func()"?
1324
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001325 CLEAR_FIELD(ea);
1326 ea.cmd = line;
1327 ea.arg = p;
1328 ea.cmdidx = CMD_def;
1329 ea.getline = eap->getline;
1330 ea.cookie = eap->cookie;
1331
1332 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001333 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
1334 is_class ? CF_CLASS : CF_INTERFACE);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001335 ga_clear_strings(&lines_to_free);
1336
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001337 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001338 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001339 char_u *name = uf->uf_name;
1340 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001341
1342 if (is_new && !is_valid_constructor(uf, is_abstract, has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001343 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001344 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001345 break;
1346 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001347
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001348 garray_T *fgap = has_static || is_new
1349 ? &classfunctions : &objmethods;
Bram Moolenaar58b40092023-01-11 15:59:05 +00001350 // Check the name isn't used already.
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001351 if (is_duplicate_method(fgap, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001352 {
1353 success = FALSE;
1354 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001355 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001356 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001357
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001358 if (ga_grow(fgap, 1) == OK)
1359 {
1360 if (is_new)
1361 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001362
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001363 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1364 ++fgap->ga_len;
1365 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001366 }
1367 }
1368
1369 // class members
1370 else if (has_static)
1371 {
1372 // class members (public, read access, private):
1373 // "static _varname"
1374 // "static varname"
1375 // "public static varname"
1376 char_u *varname = p;
1377 char_u *varname_end = NULL;
1378 type_T *type = NULL;
1379 char_u *init_expr = NULL;
1380 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001381 &varname_end, &type_list, &type,
1382 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001383 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001384 if (is_duplicate_member(&classmembers, varname, varname_end))
1385 {
1386 vim_free(init_expr);
1387 break;
1388 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001389 if (add_member(&classmembers, varname, varname_end,
1390 has_public, type, init_expr) == FAIL)
1391 {
1392 vim_free(init_expr);
1393 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001394 }
1395 }
1396
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001397 else
1398 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001399 if (is_class)
1400 semsg(_(e_not_valid_command_in_class_str), line);
1401 else
1402 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001403 break;
1404 }
1405 }
1406 vim_free(theline);
1407
Bram Moolenaar83677162023-01-08 19:54:10 +00001408 class_T *extends_cl = NULL; // class from "extends" argument
1409
1410 /*
1411 * Check a few things before defining the class.
1412 */
1413
1414 // Check the "extends" class is valid.
1415 if (success && extends != NULL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001416 success = validate_extends_class(extends, &extends_cl);
Bram Moolenaar83677162023-01-08 19:54:10 +00001417 VIM_CLEAR(extends);
1418
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001419 // Check the new class members and object members doesn't duplicate the
1420 // members in the extended class lineage.
1421 if (success && extends_cl != NULL)
1422 success = validate_extends_members(&classmembers, &objmembers,
1423 extends_cl);
1424
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001425 class_T **intf_classes = NULL;
1426
Bram Moolenaar83677162023-01-08 19:54:10 +00001427 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001428 if (success && ga_impl.ga_len > 0)
1429 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001430 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1431
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001432 success = validate_implements_classes(&ga_impl, intf_classes,
1433 &classfunctions, &classmembers,
1434 &objmethods, &objmembers);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001435 }
1436
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001437 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001438 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001439 success = check_func_arg_names(&classfunctions, &objmethods,
1440 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001441
Bram Moolenaareb533502022-12-14 15:06:11 +00001442 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001443 if (success)
1444 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001445 // "endclass" encountered without failures: Create the class.
1446
Bram Moolenaareb533502022-12-14 15:06:11 +00001447 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001448 if (cl == NULL)
1449 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001450 if (!is_class)
1451 cl->class_flags = CLASS_INTERFACE;
1452
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001453 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001454 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001455 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001456 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001457
Bram Moolenaard0200c82023-01-28 15:19:40 +00001458 if (extends_cl != NULL)
1459 {
1460 cl->class_extends = extends_cl;
1461 extends_cl->class_flags |= CLASS_EXTENDED;
1462 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001463
Bram Moolenaard505d172022-12-18 21:42:55 +00001464 // Add class and object members to "cl".
1465 if (add_members_to_class(&classmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001466 extends_cl == NULL ? NULL
1467 : extends_cl->class_class_members,
1468 extends_cl == NULL ? 0
1469 : extends_cl->class_class_member_count,
1470 &cl->class_class_members,
1471 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001472 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001473 extends_cl == NULL ? NULL
1474 : extends_cl->class_obj_members,
1475 extends_cl == NULL ? 0
1476 : extends_cl->class_obj_member_count,
1477 &cl->class_obj_members,
1478 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001479 goto cleanup;
1480
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001481 if (ga_impl.ga_len > 0)
1482 {
1483 // Move the "implements" names into the class.
1484 cl->class_interface_count = ga_impl.ga_len;
1485 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1486 if (cl->class_interfaces == NULL)
1487 goto cleanup;
1488 for (int i = 0; i < ga_impl.ga_len; ++i)
1489 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1490 VIM_CLEAR(ga_impl.ga_data);
1491 ga_impl.ga_len = 0;
1492
Bram Moolenaard0200c82023-01-28 15:19:40 +00001493 cl->class_interfaces_cl = intf_classes;
1494 intf_classes = NULL;
1495 }
1496
1497 if (cl->class_interface_count > 0 || extends_cl != NULL)
1498 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001499 // Add a method and member lookup table to each of the interface
1500 // classes.
1501 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1502 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001503 }
1504
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001505 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001506 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001507 add_class_members(cl, eap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001508
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001509 int have_new = FALSE;
1510 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001511 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001512 {
1513 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1514 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001515 {
1516 have_new = TRUE;
1517 break;
1518 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001519 }
1520
1521 if (have_new)
1522 // The return type of new() is an object of class "cl"
1523 class_func->uf_ret_type->tt_class = cl;
1524 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001525 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001526 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001527
Bram Moolenaar58b40092023-01-11 15:59:05 +00001528 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001529 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1530 &objmethods) == FAIL)
1531 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001532
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001533 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001534 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001535 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001536 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001537 cl->class_type_list = type_list;
1538
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001539 class_created(cl);
1540
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001541 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001542 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001543
1544 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001545 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02001546 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001547 typval_T tv;
1548 tv.v_type = VAR_CLASS;
1549 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001550 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001551 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001552 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001553 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001554 return;
1555 }
1556
1557cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00001558 if (cl != NULL)
1559 {
1560 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001561 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001562 if (cl->class_interfaces != NULL)
1563 {
1564 for (int i = 0; i < cl->class_interface_count; ++i)
1565 vim_free(cl->class_interfaces[i]);
1566 vim_free(cl->class_interfaces);
1567 }
1568 if (cl->class_interfaces_cl != NULL)
1569 {
1570 for (int i = 0; i < cl->class_interface_count; ++i)
1571 class_unref(cl->class_interfaces_cl[i]);
1572 vim_free(cl->class_interfaces_cl);
1573 }
Bram Moolenaareb533502022-12-14 15:06:11 +00001574 vim_free(cl->class_obj_members);
1575 vim_free(cl->class_obj_methods);
1576 vim_free(cl);
1577 }
1578
Bram Moolenaar83677162023-01-08 19:54:10 +00001579 vim_free(extends);
1580 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001581
1582 if (intf_classes != NULL)
1583 {
1584 for (int i = 0; i < ga_impl.ga_len; ++i)
1585 class_unref(intf_classes[i]);
1586 vim_free(intf_classes);
1587 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001588 ga_clear_strings(&ga_impl);
1589
Bram Moolenaard505d172022-12-18 21:42:55 +00001590 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001591 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001592 garray_T *gap = round == 1 ? &classmembers : &objmembers;
1593 if (gap->ga_len == 0 || gap->ga_data == NULL)
1594 continue;
1595
1596 for (int i = 0; i < gap->ga_len; ++i)
1597 {
1598 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
1599 vim_free(m->ocm_name);
1600 vim_free(m->ocm_init);
1601 }
1602 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001603 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001604
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001605 for (int i = 0; i < objmethods.ga_len; ++i)
1606 {
1607 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
1608 func_clear_free(uf, FALSE);
1609 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001610 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001611
1612 for (int i = 0; i < classfunctions.ga_len; ++i)
1613 {
1614 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
1615 func_clear_free(uf, FALSE);
1616 }
1617 ga_clear(&classfunctions);
1618
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001619 clear_type_list(&type_list);
1620}
1621
1622/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001623 * Find member "name" in class "cl", set "member_idx" to the member index and
1624 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001625 * When "is_object" is TRUE, then look for object members. Otherwise look for
1626 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001627 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02001628 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001629 */
1630 type_T *
1631class_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001632 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001633 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001634 char_u *name,
1635 char_u *name_end,
1636 int *member_idx,
Ernie Rael456ae552023-09-01 18:54:54 +02001637 ocmember_T **p_m)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001638{
1639 *member_idx = -1; // not found (yet)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001640 size_t len = name_end - name;
1641 int member_count = is_object ? cl->class_obj_member_count
1642 : cl->class_class_member_count;
1643 ocmember_T *members = is_object ? cl->class_obj_members
1644 : cl->class_class_members;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001645
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001646 for (int i = 0; i < member_count; ++i)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001647 {
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001648 ocmember_T *m = members + i;
Bram Moolenaard505d172022-12-18 21:42:55 +00001649 if (STRNCMP(m->ocm_name, name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001650 {
1651 *member_idx = i;
Ernie Rael456ae552023-09-01 18:54:54 +02001652 if (p_m != NULL)
1653 *p_m = m;
Bram Moolenaard505d172022-12-18 21:42:55 +00001654 return m->ocm_type;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001655 }
1656 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001657
1658 semsg(_(e_unknown_variable_str), name);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001659 return &t_any;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001660}
1661
1662/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001663 * Handle ":enum" up to ":endenum".
1664 */
1665 void
1666ex_enum(exarg_T *eap UNUSED)
1667{
1668 // TODO
1669}
1670
1671/*
1672 * Handle ":type".
1673 */
1674 void
1675ex_type(exarg_T *eap UNUSED)
1676{
1677 // TODO
1678}
1679
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001680/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001681 * Returns OK if a member variable named "name" is present in the class "cl".
1682 * Otherwise returns FAIL. If found, the member variable typval is set in
1683 * "rettv". If "is_object" is TRUE, then the object member variable table is
1684 * searched. Otherwise the class member variable table is searched.
1685 */
1686 static int
1687get_member_tv(
1688 class_T *cl,
1689 int is_object,
1690 char_u *name,
1691 size_t namelen,
1692 typval_T *rettv)
1693{
1694 int member_count = is_object ? cl->class_obj_member_count
1695 : cl->class_class_member_count;
1696 ocmember_T *members = is_object ? cl->class_obj_members
1697 : cl->class_class_members;
1698
1699 for (int i = 0; i < member_count; ++i)
1700 {
1701 ocmember_T *m = &members[i];
1702 if (STRNCMP(name, m->ocm_name, namelen) == 0
1703 && m->ocm_name[namelen] == NUL)
1704 {
1705 if (*name == '_')
1706 {
1707 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
1708 return FAIL;
1709 }
1710
1711 // The object only contains a pointer to the class, the member
1712 // values array follows right after that.
1713 object_T *obj = rettv->vval.v_object;
1714 if (is_object)
1715 {
1716 typval_T *tv = (typval_T *)(obj + 1) + i;
1717 copy_tv(tv, rettv);
1718 }
1719 else
1720 copy_tv(&cl->class_members_tv[i], rettv);
1721
1722 object_unref(obj);
1723
1724 return OK;
1725 }
1726 }
1727
1728 return FAIL;
1729}
1730
1731/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001732 * Evaluate what comes after a class:
1733 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001734 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001735 * - class constructor: SomeClass.new()
1736 * - object member: someObject.varname
1737 * - object method: someObject.SomeMethod()
1738 *
1739 * "*arg" points to the '.'.
1740 * "*arg" is advanced to after the member name or method call.
1741 *
1742 * Returns FAIL or OK.
1743 */
1744 int
1745class_object_index(
1746 char_u **arg,
1747 typval_T *rettv,
1748 evalarg_T *evalarg,
1749 int verbose UNUSED) // give error messages
1750{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001751 if (VIM_ISWHITE((*arg)[1]))
1752 {
1753 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
1754 return FAIL;
1755 }
1756
1757 ++*arg;
1758 char_u *name = *arg;
1759 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
1760 if (name_end == name)
1761 return FAIL;
1762 size_t len = name_end - name;
1763
Bram Moolenaar552bdca2023-02-17 21:08:50 +00001764 class_T *cl;
1765 if (rettv->v_type == VAR_CLASS)
1766 cl = rettv->vval.v_class;
1767 else // VAR_OBJECT
1768 {
1769 if (rettv->vval.v_object == NULL)
1770 {
1771 emsg(_(e_using_null_object));
1772 return FAIL;
1773 }
1774 cl = rettv->vval.v_object->obj_class;
1775 }
1776
Bram Moolenaard13dd302023-03-11 20:56:35 +00001777 if (cl == NULL)
1778 {
1779 emsg(_(e_incomplete_type));
1780 return FAIL;
1781 }
1782
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001783 if (*name_end == '(')
1784 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001785 int on_class = rettv->v_type == VAR_CLASS;
1786 int count = on_class ? cl->class_class_function_count
1787 : cl->class_obj_method_count;
1788 for (int i = 0; i < count; ++i)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001789 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001790 ufunc_T *fp = on_class ? cl->class_class_functions[i]
1791 : cl->class_obj_methods[i];
Bram Moolenaar4ae00572022-12-09 22:49:23 +00001792 // Use a separate pointer to avoid that ASAN complains about
1793 // uf_name[] only being 4 characters.
1794 char_u *ufname = (char_u *)fp->uf_name;
1795 if (STRNCMP(name, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001796 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001797 typval_T argvars[MAX_FUNC_ARGS + 1];
1798 int argcount = 0;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001799
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001800 if (*ufname == '_')
Yegappan Lakshmanancd7293b2023-08-27 19:18:23 +02001801 {
1802 // Cannot access a private method outside of a class
1803 semsg(_(e_cannot_access_private_method_str), name);
1804 return FAIL;
1805 }
1806
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001807 char_u *argp = name_end;
1808 int ret = get_func_arguments(&argp, evalarg, 0,
1809 argvars, &argcount);
1810 if (ret == FAIL)
1811 return FAIL;
1812
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001813 funcexe_T funcexe;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001814 CLEAR_FIELD(funcexe);
1815 funcexe.fe_evaluate = TRUE;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001816 if (rettv->v_type == VAR_OBJECT)
1817 {
1818 funcexe.fe_object = rettv->vval.v_object;
1819 ++funcexe.fe_object->obj_refcount;
1820 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001821
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001822 // Clear the class or object after calling the function, in
1823 // case the refcount is one.
1824 typval_T tv_tofree = *rettv;
1825 rettv->v_type = VAR_UNKNOWN;
1826
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001827 // Call the user function. Result goes into rettv;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001828 int error = call_user_func_check(fp, argcount, argvars,
1829 rettv, &funcexe, NULL);
1830
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001831 // Clear the previous rettv and the arguments.
1832 clear_tv(&tv_tofree);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001833 for (int idx = 0; idx < argcount; ++idx)
1834 clear_tv(&argvars[idx]);
1835
1836 if (error != FCERR_NONE)
1837 {
1838 user_func_error(error, printable_func_name(fp),
1839 funcexe.fe_found_var);
1840 return FAIL;
1841 }
1842 *arg = argp;
1843 return OK;
1844 }
1845 }
1846
1847 semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name);
1848 }
1849
1850 else if (rettv->v_type == VAR_OBJECT)
1851 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001852 // Search in the object member variable table and the class member
1853 // variable table.
1854 if (get_member_tv(cl, TRUE, name, len, rettv) == OK
1855 || get_member_tv(cl, FALSE, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001856 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001857 *arg = name_end;
1858 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001859 }
1860
1861 semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
1862 }
1863
Bram Moolenaard505d172022-12-18 21:42:55 +00001864 else if (rettv->v_type == VAR_CLASS)
1865 {
1866 // class member
1867 for (int i = 0; i < cl->class_class_member_count; ++i)
1868 {
1869 ocmember_T *m = &cl->class_class_members[i];
1870 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
1871 {
1872 if (*name == '_')
1873 {
1874 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
1875 return FAIL;
1876 }
Ernie Rael18143d32023-09-04 22:30:41 +02001877 if ((cl->class_flags & CLASS_INTERFACE) != 0)
1878 {
1879 semsg(_(e_interface_static_direct_access_str),
1880 cl->class_name, m->ocm_name);
1881 return FAIL;
1882 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001883
1884 typval_T *tv = &cl->class_members_tv[i];
1885 copy_tv(tv, rettv);
1886 class_unref(cl);
1887
1888 *arg = name_end;
1889 return OK;
1890 }
1891 }
1892
1893 semsg(_(e_member_not_found_on_class_str_str), cl->class_name, name);
1894 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001895
1896 return FAIL;
1897}
1898
1899/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001900 * If "arg" points to a class or object method, return it.
1901 * Otherwise return NULL.
1902 */
1903 ufunc_T *
1904find_class_func(char_u **arg)
1905{
1906 char_u *name = *arg;
1907 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
1908 if (name_end == name || *name_end != '.')
1909 return NULL;
1910
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001911 size_t len = name_end - name;
1912 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001913 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00001914 if (eval_variable(name, (int)len,
1915 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001916 return NULL;
1917 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00001918 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001919
1920 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
1921 : tv.vval.v_object->obj_class;
1922 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00001923 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001924 char_u *fname = name_end + 1;
1925 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
1926 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00001927 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001928 len = fname_end - fname;
1929
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001930 int count = tv.v_type == VAR_CLASS ? cl->class_class_function_count
1931 : cl->class_obj_method_count;
1932 ufunc_T **funcs = tv.v_type == VAR_CLASS ? cl->class_class_functions
1933 : cl->class_obj_methods;
1934 for (int i = 0; i < count; ++i)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001935 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001936 ufunc_T *fp = funcs[i];
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001937 // Use a separate pointer to avoid that ASAN complains about
1938 // uf_name[] only being 4 characters.
1939 char_u *ufname = (char_u *)fp->uf_name;
1940 if (STRNCMP(fname, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaareb533502022-12-14 15:06:11 +00001941 {
1942 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001943 return fp;
Bram Moolenaareb533502022-12-14 15:06:11 +00001944 }
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001945 }
1946
Bram Moolenaareb533502022-12-14 15:06:11 +00001947fail_after_eval:
1948 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001949 return NULL;
1950}
1951
1952/*
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001953 * If "name[len]" is a class member in cctx->ctx_ufunc->uf_class return the
1954 * index in class.class_class_members[].
1955 * If "cl_ret" is not NULL set it to the class.
1956 * Otherwise return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001957 */
1958 int
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001959class_member_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001960{
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001961 if (cctx == NULL || cctx->ctx_ufunc == NULL
1962 || cctx->ctx_ufunc->uf_class == NULL)
1963 return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001964 class_T *cl = cctx->ctx_ufunc->uf_class;
1965
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001966 for (int i = 0; i < cl->class_class_member_count; ++i)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001967 {
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001968 ocmember_T *m = &cl->class_class_members[i];
1969 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001970 {
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001971 if (cl_ret != NULL)
1972 *cl_ret = cl;
1973 return i;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001974 }
1975 }
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001976 return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001977}
1978
1979/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00001980 * Return TRUE if current context "cctx_arg" is inside class "cl".
1981 * Return FALSE if not.
1982 */
1983 int
1984inside_class(cctx_T *cctx_arg, class_T *cl)
1985{
1986 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02001987 if (cctx->ctx_ufunc != NULL
1988 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00001989 return TRUE;
1990 return FALSE;
1991}
1992
1993/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001994 * Make a copy of an object.
1995 */
1996 void
1997copy_object(typval_T *from, typval_T *to)
1998{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02001999 if (from->vval.v_object == NULL)
2000 to->vval.v_object = NULL;
2001 else
2002 {
2003 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002004 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002005 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002006}
2007
2008/*
2009 * Free an object.
2010 */
2011 static void
2012object_clear(object_T *obj)
2013{
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002014 // Avoid a recursive call, it can happen if "obj" has a circular reference.
2015 obj->obj_refcount = INT_MAX;
2016
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002017 class_T *cl = obj->obj_class;
2018
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002019 if (!cl)
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002020 return;
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002021
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002022 // the member values are just after the object structure
2023 typval_T *tv = (typval_T *)(obj + 1);
2024 for (int i = 0; i < cl->class_obj_member_count; ++i)
2025 clear_tv(tv + i);
2026
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002027 // Remove from the list headed by "first_object".
2028 object_cleared(obj);
2029
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002030 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002031 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002032}
2033
2034/*
2035 * Unreference an object.
2036 */
2037 void
2038object_unref(object_T *obj)
2039{
2040 if (obj != NULL && --obj->obj_refcount <= 0)
2041 object_clear(obj);
2042}
2043
2044/*
2045 * Make a copy of a class.
2046 */
2047 void
2048copy_class(typval_T *from, typval_T *to)
2049{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002050 if (from->vval.v_class == NULL)
2051 to->vval.v_class = NULL;
2052 else
2053 {
2054 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002055 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002056 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002057}
2058
2059/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002060 * Free the class "cl" and its contents.
2061 */
2062 static void
2063class_free(class_T *cl)
2064{
2065 // Freeing what the class contains may recursively come back here.
2066 // Clear "class_name" first, if it is NULL the class does not need to
2067 // be freed.
2068 VIM_CLEAR(cl->class_name);
2069
2070 class_unref(cl->class_extends);
2071
2072 for (int i = 0; i < cl->class_interface_count; ++i)
2073 {
2074 vim_free(((char_u **)cl->class_interfaces)[i]);
2075 if (cl->class_interfaces_cl[i] != NULL)
2076 class_unref(cl->class_interfaces_cl[i]);
2077 }
2078 vim_free(cl->class_interfaces);
2079 vim_free(cl->class_interfaces_cl);
2080
2081 itf2class_T *next;
2082 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2083 {
2084 next = i2c->i2c_next;
2085 vim_free(i2c);
2086 }
2087
2088 for (int i = 0; i < cl->class_class_member_count; ++i)
2089 {
2090 ocmember_T *m = &cl->class_class_members[i];
2091 vim_free(m->ocm_name);
2092 vim_free(m->ocm_init);
2093 if (cl->class_members_tv != NULL)
2094 clear_tv(&cl->class_members_tv[i]);
2095 }
2096 vim_free(cl->class_class_members);
2097 vim_free(cl->class_members_tv);
2098
2099 for (int i = 0; i < cl->class_obj_member_count; ++i)
2100 {
2101 ocmember_T *m = &cl->class_obj_members[i];
2102 vim_free(m->ocm_name);
2103 vim_free(m->ocm_init);
2104 }
2105 vim_free(cl->class_obj_members);
2106
2107 for (int i = 0; i < cl->class_class_function_count; ++i)
2108 {
2109 ufunc_T *uf = cl->class_class_functions[i];
2110 func_clear_free(uf, FALSE);
2111 }
2112 vim_free(cl->class_class_functions);
2113
2114 for (int i = 0; i < cl->class_obj_method_count; ++i)
2115 {
2116 ufunc_T *uf = cl->class_obj_methods[i];
2117 func_clear_free(uf, FALSE);
2118 }
2119 vim_free(cl->class_obj_methods);
2120
2121 clear_type_list(&cl->class_type_list);
2122
2123 class_cleared(cl);
2124
2125 vim_free(cl);
2126}
2127
2128/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002129 * Unreference a class. Free it when the reference count goes down to zero.
2130 */
2131 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002132class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002133{
Bram Moolenaard505d172022-12-18 21:42:55 +00002134 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002135 class_free(cl);
2136}
2137
2138/*
2139 * Go through the list of all classes and free items without "copyID".
2140 */
2141 int
2142class_free_nonref(int copyID)
2143{
2144 int did_free = FALSE;
2145
2146 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002147 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002148 next_nonref_class = cl->class_next_used;
2149 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002150 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002151 // Free the class and items it contains.
2152 class_free(cl);
2153 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002154 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002155 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002156
2157 next_nonref_class = NULL;
2158 return did_free;
2159}
2160
2161 int
2162set_ref_in_classes(int copyID)
2163{
2164 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
2165 set_ref_in_item_class(cl, copyID, NULL, NULL);
2166
2167 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002168}
2169
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002170static object_T *first_object = NULL;
2171
2172/*
2173 * Call this function when an object has been created. It will be added to the
2174 * list headed by "first_object".
2175 */
2176 void
2177object_created(object_T *obj)
2178{
2179 if (first_object != NULL)
2180 {
2181 obj->obj_next_used = first_object;
2182 first_object->obj_prev_used = obj;
2183 }
2184 first_object = obj;
2185}
2186
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002187static object_T *next_nonref_obj = NULL;
2188
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002189/*
2190 * Call this function when an object has been cleared and is about to be freed.
2191 * It is removed from the list headed by "first_object".
2192 */
2193 void
2194object_cleared(object_T *obj)
2195{
2196 if (obj->obj_next_used != NULL)
2197 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
2198 if (obj->obj_prev_used != NULL)
2199 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
2200 else if (first_object == obj)
2201 first_object = obj->obj_next_used;
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002202
2203 // update the next object to check if needed
2204 if (obj == next_nonref_obj)
2205 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002206}
2207
2208/*
2209 * Go through the list of all objects and free items without "copyID".
2210 */
2211 int
2212object_free_nonref(int copyID)
2213{
2214 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002215
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002216 for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002217 {
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002218 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002219 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
2220 {
2221 // Free the object and items it contains.
2222 object_clear(obj);
2223 did_free = TRUE;
2224 }
2225 }
2226
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002227 next_nonref_obj = NULL;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002228 return did_free;
2229}
2230
LemonBoyafe04662023-08-23 21:08:11 +02002231/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002232 * Return TRUE when the class "cl", its base class or one of the implemented
2233 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02002234 */
2235 int
2236class_instance_of(class_T *cl, class_T *other_cl)
2237{
2238 if (cl == other_cl)
2239 return TRUE;
2240
2241 // Recursively check the base classes.
2242 for (; cl != NULL; cl = cl->class_extends)
2243 {
2244 if (cl == other_cl)
2245 return TRUE;
2246 // Check the implemented interfaces.
2247 for (int i = cl->class_interface_count - 1; i >= 0; --i)
2248 if (cl->class_interfaces_cl[i] == other_cl)
2249 return TRUE;
2250 }
2251
2252 return FALSE;
2253}
2254
2255/*
2256 * "instanceof(object, classinfo)" function
2257 */
2258 void
2259f_instanceof(typval_T *argvars, typval_T *rettv)
2260{
2261 typval_T *object_tv = &argvars[0];
2262 typval_T *classinfo_tv = &argvars[1];
2263 listitem_T *li;
2264
2265 rettv->vval.v_number = VVAL_FALSE;
2266
2267 if (check_for_object_arg(argvars, 0) == FAIL
2268 || check_for_class_or_list_arg(argvars, 1) == FAIL)
2269 return;
2270
2271 if (classinfo_tv->v_type == VAR_LIST)
2272 {
2273 FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
2274 {
2275 if (li->li_tv.v_type != VAR_CLASS)
2276 {
2277 emsg(_(e_class_required));
2278 return;
2279 }
2280
2281 if (class_instance_of(object_tv->vval.v_object->obj_class,
2282 li->li_tv.vval.v_class) == TRUE)
2283 {
2284 rettv->vval.v_number = VVAL_TRUE;
2285 return;
2286 }
2287 }
2288 }
2289 else if (classinfo_tv->v_type == VAR_CLASS)
2290 {
2291 rettv->vval.v_number = class_instance_of(object_tv->vval.v_object->obj_class,
2292 classinfo_tv->vval.v_class);
2293 }
2294}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002295
2296#endif // FEAT_EVAL