blob: 77fd5461b395e9701ff6befe9c04180b8bb63ce9 [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
Bram Moolenaard0200c82023-01-28 15:19:40 +0000223object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000224{
Bram Moolenaard0200c82023-01-28 15:19:40 +0000225 if (idx > (is_method ? itf->class_obj_method_count
226 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000227 {
228 siemsg("index %d out of range for interface %s", idx, itf->class_name);
229 return 0;
230 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200231
232 // If "cl" is the interface or the class that is extended, then the method
233 // index can be used directly and there is no need to search for the method
234 // index in one of the child classes.
235 if (cl == itf)
236 return idx;
237
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000238 itf2class_T *i2c;
239 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Bram Moolenaard0200c82023-01-28 15:19:40 +0000240 if (i2c->i2c_class == cl && i2c->i2c_is_method == is_method)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000241 break;
242 if (i2c == NULL)
243 {
244 siemsg("class %s not found on interface %s",
245 cl->class_name, itf->class_name);
246 return 0;
247 }
248 int *table = (int *)(i2c + 1);
249 return table[idx];
250}
251
252/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200253 * Check whether a class named "extends_name" is present. If the class is
254 * valid, then "extends_clp" is set with the class pointer.
255 * Returns TRUE if the class name "extends_names" is a valid class.
256 */
257 static int
258validate_extends_class(char_u *extends_name, class_T **extends_clp)
259{
260 typval_T tv;
261 int success = FALSE;
262
263 tv.v_type = VAR_UNKNOWN;
264 if (eval_variable_import(extends_name, &tv) == FAIL)
265 {
266 semsg(_(e_class_name_not_found_str), extends_name);
267 return success;
268 }
269 else
270 {
271 if (tv.v_type != VAR_CLASS
272 || tv.vval.v_class == NULL
273 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
274 semsg(_(e_cannot_extend_str), extends_name);
275 else
276 {
277 class_T *extends_cl = tv.vval.v_class;
278 ++extends_cl->class_refcount;
279 *extends_clp = extends_cl;
280 success = TRUE;
281 }
282 clear_tv(&tv);
283 }
284
285 return success;
286}
287
288/*
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200289 * Check whether a class/object member variable in "classmembers_gap" /
290 * "objmembers_gap" is a duplicate of a member in any of the extended parent
291 * class lineage. Returns TRUE if there are no duplicates.
292 */
293 static int
294validate_extends_members(
295 garray_T *classmembers_gap,
296 garray_T *objmembers_gap,
297 class_T *extends_cl)
298{
299 for (int loop = 1; loop <= 2; ++loop)
300 {
301 // loop == 1: check class members
302 // loop == 2: check object members
303 int member_count = loop == 1 ? classmembers_gap->ga_len
304 : objmembers_gap->ga_len;
305 if (member_count == 0)
306 continue;
307 ocmember_T *members = (ocmember_T *)(loop == 1
308 ? classmembers_gap->ga_data
309 : objmembers_gap->ga_data);
310
311 // Validate each member variable
312 for (int c_i = 0; c_i < member_count; c_i++)
313 {
314 class_T *p_cl = extends_cl;
315 ocmember_T *c_m = members + c_i;
316 char_u *pstr = (*c_m->ocm_name == '_')
317 ? c_m->ocm_name + 1 : c_m->ocm_name;
318
319 // Check in all the parent classes in the lineage
320 while (p_cl != NULL)
321 {
322 int p_member_count = loop == 1
323 ? p_cl->class_class_member_count
324 : p_cl->class_obj_member_count;
325 if (p_member_count == 0)
326 continue;
327 ocmember_T *p_members = (loop == 1
328 ? p_cl->class_class_members
329 : p_cl->class_obj_members);
330
331 // Compare against all the members in the parent class
332 for (int p_i = 0; p_i < p_member_count; p_i++)
333 {
334 ocmember_T *p_m = p_members + p_i;
335 char_u *qstr = (*p_m->ocm_name == '_')
336 ? p_m->ocm_name + 1 : p_m->ocm_name;
337 if (STRCMP(pstr, qstr) == 0)
338 {
339 semsg(_(e_duplicate_member_str), c_m->ocm_name);
340 return FALSE;
341 }
342 }
343
344 p_cl = p_cl->class_extends;
345 }
346 }
347 }
348
349 return TRUE;
350}
351
352/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200353 * Check the members of the interface class "ifcl" match the class members
354 * ("classmembers_gap") and object members ("objmembers_gap") of a class.
355 * Returns TRUE if the class and object member names are valid.
356 */
357 static int
358validate_interface_members(
359 char_u *intf_class_name,
360 class_T *ifcl,
361 garray_T *classmembers_gap,
362 garray_T *objmembers_gap)
363{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200364 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200365 {
366 // loop == 1: check class members
367 // loop == 2: check object members
368 int if_count = loop == 1 ? ifcl->class_class_member_count
369 : ifcl->class_obj_member_count;
370 if (if_count == 0)
371 continue;
372 ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
373 : ifcl->class_obj_members;
374 ocmember_T *cl_ms = (ocmember_T *)(loop == 1
375 ? classmembers_gap->ga_data
376 : objmembers_gap->ga_data);
377 int cl_count = loop == 1 ? classmembers_gap->ga_len
378 : objmembers_gap->ga_len;
379 for (int if_i = 0; if_i < if_count; ++if_i)
380 {
381 int cl_i;
382 for (cl_i = 0; cl_i < cl_count; ++cl_i)
383 {
384 ocmember_T *m = &cl_ms[cl_i];
385 where_T where = WHERE_INIT;
386
387 if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) != 0)
388 continue;
389
390 // Ensure the type is matching.
391 where.wt_func_name = (char *)m->ocm_name;
392 where.wt_kind = WT_MEMBER;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200393 if (check_type(if_ms[if_i].ocm_type, m->ocm_type, TRUE,
394 where) == FAIL)
395 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200396
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200397 if (if_ms[if_i].ocm_access != m->ocm_access)
398 {
399 semsg(_(e_member_str_of_interface_str_has_different_access),
400 if_ms[if_i].ocm_name, intf_class_name);
401 return FALSE;
402 }
403
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200404 break;
405 }
406 if (cl_i == cl_count)
407 {
408 semsg(_(e_member_str_of_interface_str_not_implemented),
409 if_ms[if_i].ocm_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200410 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200411 }
412 }
413 }
414
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200415 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200416}
417
418/*
419 * Check the functions/methods of the interface class "ifcl" match the class
420 * methods ("classfunctions_gap") and object functions ("objmemthods_gap") of a
421 * class.
422 * Returns TRUE if the class and object member names are valid.
423 */
424 static int
425validate_interface_methods(
426 char_u *intf_class_name,
427 class_T *ifcl,
428 garray_T *classfunctions_gap,
429 garray_T *objmethods_gap)
430{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200431 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200432 {
433 // loop == 1: check class functions
434 // loop == 2: check object methods
435 int if_count = loop == 1 ? ifcl->class_class_function_count
436 : ifcl->class_obj_method_count;
437 if (if_count == 0)
438 continue;
439 ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
440 : ifcl->class_obj_methods;
441 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
442 ? classfunctions_gap->ga_data
443 : objmethods_gap->ga_data);
444 int cl_count = loop == 1 ? classfunctions_gap->ga_len
445 : objmethods_gap->ga_len;
446 for (int if_i = 0; if_i < if_count; ++if_i)
447 {
448 char_u *if_name = if_fp[if_i]->uf_name;
449 int cl_i;
450 for (cl_i = 0; cl_i < cl_count; ++cl_i)
451 {
452 char_u *cl_name = cl_fp[cl_i]->uf_name;
453 if (STRCMP(if_name, cl_name) == 0)
454 {
455 where_T where = WHERE_INIT;
456
457 // Ensure the type is matching.
458 where.wt_func_name = (char *)if_name;
459 where.wt_kind = WT_METHOD;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200460 if (check_type(if_fp[if_i]->uf_func_type,
461 cl_fp[cl_i]->uf_func_type, TRUE, where) == FAIL)
462 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200463 break;
464 }
465 }
466 if (cl_i == cl_count)
467 {
468 semsg(_(e_function_str_of_interface_str_not_implemented),
469 if_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200470 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200471 }
472 }
473 }
474
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200475 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200476}
477
478/*
479 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200480 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200481 * object methods and object members in the new class are in
482 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
483 * "objmembers_gap" respectively.
484 */
485 static int
486validate_implements_classes(
487 garray_T *impl_gap,
488 class_T **intf_classes,
489 garray_T *classfunctions_gap,
490 garray_T *classmembers_gap,
491 garray_T *objmethods_gap,
492 garray_T *objmembers_gap)
493{
494 int success = TRUE;
495
496 for (int i = 0; i < impl_gap->ga_len && success; ++i)
497 {
498 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
499 typval_T tv;
500 tv.v_type = VAR_UNKNOWN;
501 if (eval_variable_import(impl, &tv) == FAIL)
502 {
503 semsg(_(e_interface_name_not_found_str), impl);
504 success = FALSE;
505 break;
506 }
507
508 if (tv.v_type != VAR_CLASS
509 || tv.vval.v_class == NULL
510 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
511 {
512 semsg(_(e_not_valid_interface_str), impl);
513 success = FALSE;
514 clear_tv(&tv);
515 break;
516 }
517
518 class_T *ifcl = tv.vval.v_class;
519 intf_classes[i] = ifcl;
520 ++ifcl->class_refcount;
521
522 // check the members of the interface match the members of the class
523 success = validate_interface_members(impl, ifcl, classmembers_gap,
524 objmembers_gap);
525
526 // check the functions/methods of the interface match the
527 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200528 if (success)
529 success = validate_interface_methods(impl, ifcl,
530 classfunctions_gap, objmethods_gap);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200531 clear_tv(&tv);
532 }
533
534 return success;
535}
536
537/*
538 * Check no function argument name is used as a class member.
539 * (Object members are always accessed with "this." prefix, so no need
540 * to check them.)
541 */
542 static int
543check_func_arg_names(
544 garray_T *classfunctions_gap,
545 garray_T *objmethods_gap,
546 garray_T *classmembers_gap)
547{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200548 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200549 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200550 {
551 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
552
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200553 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200554 {
555 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
556
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200557 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200558 {
559 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
560 garray_T *mgap = classmembers_gap;
561
562 // Check all the class member names
563 for (int mi = 0; mi < mgap->ga_len; ++mi)
564 {
565 char_u *mname = ((ocmember_T *)mgap->ga_data + mi)
566 ->ocm_name;
567 if (STRCMP(aname, mname) == 0)
568 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200569 if (uf->uf_script_ctx.sc_sid > 0)
570 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
571
572 semsg(_(e_argument_already_declared_in_class_str),
573 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200574
575 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200576 }
577 }
578 }
579 }
580 }
581
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200582 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200583}
584
585/*
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200586 * Returns TRUE if the member "varname" is already defined.
587 */
588 static int
589is_duplicate_member(garray_T *mgap, char_u *varname, char_u *varname_end)
590{
591 char_u *pstr = (*varname == '_') ? varname + 1 : varname;
592
593 for (int i = 0; i < mgap->ga_len; ++i)
594 {
595 ocmember_T *m = ((ocmember_T *)mgap->ga_data) + i;
596 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1 : m->ocm_name;
597 if (STRNCMP(pstr, qstr, varname_end - pstr) == 0)
598 {
599 char_u *name = vim_strnsave(varname, varname_end - varname);
600 semsg(_(e_duplicate_member_str), name);
601 vim_free(name);
602 return TRUE;
603 }
604 }
605
606 return FALSE;
607}
608
609/*
610 * Returns TRUE if the method "name" is already defined.
611 */
612 static int
613is_duplicate_method(garray_T *fgap, char_u *name)
614{
615 char_u *pstr = (*name == '_') ? name + 1 : name;
616
617 for (int i = 0; i < fgap->ga_len; ++i)
618 {
619 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
620 char_u *qstr = *n == '_' ? n + 1 : n;
621 if (STRCMP(pstr, qstr) == 0)
622 {
623 semsg(_(e_duplicate_function_str), name);
624 return TRUE;
625 }
626 }
627
628 return FALSE;
629}
630
631/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200632 * Returns TRUE if the constructor is valid.
633 */
634 static int
635is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
636{
637 // Constructors are not allowed in abstract classes.
638 if (is_abstract)
639 {
640 emsg(_(e_cannot_define_new_function_in_abstract_class));
641 return FALSE;
642 }
643 // A constructor is always static, no need to define it so.
644 if (has_static)
645 {
646 emsg(_(e_cannot_define_new_function_as_static));
647 return FALSE;
648 }
649 // A return type should not be specified for the new()
650 // constructor method.
651 if (uf->uf_ret_type->tt_type != VAR_VOID)
652 {
653 emsg(_(e_cannot_use_a_return_type_with_new));
654 return FALSE;
655 }
656 return TRUE;
657}
658
659/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200660 * Update the interface class lookup table for the member index on the
661 * interface to the member index in the class implementing the interface.
662 * And a lookup table for the object method index on the interface
663 * to the object method index in the class implementing the interface.
664 * This is also used for updating the lookup table for the extended class
665 * hierarchy.
666 */
667 static int
668update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200669 class_T *ifcl,
670 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200671 garray_T *objmethods,
672 int pobj_method_offset,
673 int is_interface)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200674{
675 if (ifcl == NULL)
676 return OK;
677
678 // Table for members.
679 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
680 + ifcl->class_obj_member_count * sizeof(int));
681 if (if2cl == NULL)
682 return FAIL;
683 if2cl->i2c_next = ifcl->class_itf2class;
684 ifcl->class_itf2class = if2cl;
685 if2cl->i2c_class = cl;
686 if2cl->i2c_is_method = FALSE;
687
688 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
689 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
690 {
691 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
692 cl->class_obj_members[cl_i].ocm_name) == 0)
693 {
694 int *table = (int *)(if2cl + 1);
695 table[if_i] = cl_i;
696 break;
697 }
698 }
699
700 // Table for methods.
701 if2cl = alloc_clear(sizeof(itf2class_T)
702 + ifcl->class_obj_method_count * sizeof(int));
703 if (if2cl == NULL)
704 return FAIL;
705 if2cl->i2c_next = ifcl->class_itf2class;
706 ifcl->class_itf2class = if2cl;
707 if2cl->i2c_class = cl;
708 if2cl->i2c_is_method = TRUE;
709
710 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
711 {
712 int done = FALSE;
713 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
714 {
715 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
716 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name)
717 == 0)
718 {
719 int *table = (int *)(if2cl + 1);
720 table[if_i] = cl_i;
721 done = TRUE;
722 break;
723 }
724 }
725
726 // extended class object method is not overridden by the child class.
727 // Keep the method declared in one of the parent classes in the
728 // lineage.
729 if (!done && !is_interface)
730 {
731 // If "ifcl" is not the immediate parent of "cl", then search in
732 // the intermediate parent classes.
733 if (cl->class_extends != ifcl)
734 {
735 class_T *parent = cl->class_extends;
736 int method_offset = objmethods->ga_len;
737
738 while (!done && parent != NULL && parent != ifcl)
739 {
740
741 for (int cl_i = 0;
742 cl_i < parent->class_obj_method_count_child; ++cl_i)
743 {
744 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
745 parent->class_obj_methods[cl_i]->uf_name)
746 == 0)
747 {
748 int *table = (int *)(if2cl + 1);
749 table[if_i] = method_offset + cl_i;
750 done = TRUE;
751 break;
752 }
753 }
754 method_offset += parent->class_obj_method_count_child;
755 parent = parent->class_extends;
756 }
757 }
758
759 if (!done)
760 {
761 int *table = (int *)(if2cl + 1);
762 table[if_i] = pobj_method_offset + if_i;
763 }
764 }
765 }
766
767 return OK;
768}
769
770/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200771 * Update the member and object method lookup tables for a new class in the
772 * interface class.
773 * For each interface add a lookup table for the member index on the interface
774 * to the member index in the new class. And a lookup table for the object
775 * method index on the interface to the object method index in the new class.
776 */
777 static int
778add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
779{
780 for (int i = 0; i < cl->class_interface_count; ++i)
781 {
782 class_T *ifcl = cl->class_interfaces_cl[i];
783
784 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
785 0, TRUE) == FAIL)
786 return FAIL;
787 }
788
789 // Update the lookup table for the extended class, if nay
790 if (extends_cl != NULL)
791 {
792 class_T *pclass = extends_cl;
793 int pobj_method_offset = objmethods_gap->ga_len;
794
795 // Update the entire lineage of extended classes.
796 while (pclass != NULL)
797 {
798 if (update_member_method_lookup_table(pclass, cl,
799 objmethods_gap, pobj_method_offset, FALSE) == FAIL)
800 return FAIL;
801
802 pobj_method_offset += pclass->class_obj_method_count_child;
803 pclass = pclass->class_extends;
804 }
805 }
806
807 return OK;
808}
809
810/*
811 * Add class members to a new class. Allocate a typval for each class member
812 * and initialize it.
813 */
814 static void
815add_class_members(class_T *cl, exarg_T *eap)
816{
817 // Allocate a typval for each class member and initialize it.
818 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
819 cl->class_class_member_count);
820 if (cl->class_members_tv == NULL)
821 return;
822
823 for (int i = 0; i < cl->class_class_member_count; ++i)
824 {
825 ocmember_T *m = &cl->class_class_members[i];
826 typval_T *tv = &cl->class_members_tv[i];
827 if (m->ocm_init != NULL)
828 {
829 typval_T *etv = eval_expr(m->ocm_init, eap);
830 if (etv != NULL)
831 {
832 *tv = *etv;
833 vim_free(etv);
834 }
835 }
836 else
837 {
838 // TODO: proper default value
839 tv->v_type = m->ocm_type->tt_type;
840 tv->vval.v_string = NULL;
841 }
842 }
843}
844
845/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +0200846 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200847 */
848 static void
849add_default_constructor(
850 class_T *cl,
851 garray_T *classfunctions_gap,
852 garray_T *type_list_gap)
853{
854 garray_T fga;
855
856 ga_init2(&fga, 1, 1000);
857 ga_concat(&fga, (char_u *)"new(");
858 for (int i = 0; i < cl->class_obj_member_count; ++i)
859 {
860 if (i > 0)
861 ga_concat(&fga, (char_u *)", ");
862 ga_concat(&fga, (char_u *)"this.");
863 ocmember_T *m = cl->class_obj_members + i;
864 ga_concat(&fga, (char_u *)m->ocm_name);
865 ga_concat(&fga, (char_u *)" = v:none");
866 }
867 ga_concat(&fga, (char_u *)")\nenddef\n");
868 ga_append(&fga, NUL);
869
870 exarg_T fea;
871 CLEAR_FIELD(fea);
872 fea.cmdidx = CMD_def;
873 fea.cmd = fea.arg = fga.ga_data;
874
875 garray_T lines_to_free;
876 ga_init2(&lines_to_free, sizeof(char_u *), 50);
877
878 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS);
879
880 ga_clear_strings(&lines_to_free);
881 vim_free(fga.ga_data);
882
883 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
884 {
885 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
886 = nf;
887 ++classfunctions_gap->ga_len;
888
889 nf->uf_flags |= FC_NEW;
890 nf->uf_ret_type = get_type_ptr(type_list_gap);
891 if (nf->uf_ret_type != NULL)
892 {
893 nf->uf_ret_type->tt_type = VAR_OBJECT;
894 nf->uf_ret_type->tt_class = cl;
895 nf->uf_ret_type->tt_argcount = 0;
896 nf->uf_ret_type->tt_args = NULL;
897 }
898 }
899}
900
901/*
902 * Add the class functions and object methods to the new class "cl".
903 * When extending a class, add the functions and methods from the parent class
904 * also.
905 */
906 static int
907add_classfuncs_objmethods(
908 class_T *cl,
909 class_T *extends_cl,
910 garray_T *classfunctions_gap,
911 garray_T *objmethods_gap)
912{
913 // loop 1: class functions, loop 2: object methods
914 for (int loop = 1; loop <= 2; ++loop)
915 {
916 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
917 int *fcount = loop == 1 ? &cl->class_class_function_count
918 : &cl->class_obj_method_count;
919 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
920 : &cl->class_obj_methods;
921
922 int parent_count = 0;
923 if (extends_cl != NULL)
924 // Include functions from the parent.
925 parent_count = loop == 1
926 ? extends_cl->class_class_function_count
927 : extends_cl->class_obj_method_count;
928
929 *fcount = parent_count + gap->ga_len;
930 if (*fcount == 0)
931 {
932 *fup = NULL;
933 continue;
934 }
935 *fup = ALLOC_MULT(ufunc_T *, *fcount);
936 if (*fup == NULL)
937 return FAIL;
938
939 if (gap->ga_len != 0)
940 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
941 vim_free(gap->ga_data);
942 if (loop == 1)
943 cl->class_class_function_count_child = gap->ga_len;
944 else
945 cl->class_obj_method_count_child = gap->ga_len;
946
947 int skipped = 0;
948 for (int i = 0; i < parent_count; ++i)
949 {
950 // Copy functions from the parent. Can't use the same
951 // function, because "uf_class" is different and compilation
952 // will have a different result.
953 // Put them after the functions in the current class, object
954 // methods may be overruled, then "super.Method()" is used to
955 // find a method from the parent.
956 // Skip "new" functions. TODO: not all of them.
957 if (loop == 1 && STRNCMP(
958 extends_cl->class_class_functions[i]->uf_name,
959 "new", 3) == 0)
960 ++skipped;
961 else
962 {
963 ufunc_T *pf = (loop == 1
964 ? extends_cl->class_class_functions
965 : extends_cl->class_obj_methods)[i];
966 (*fup)[gap->ga_len + i - skipped] = copy_function(pf);
967
968 // If the child class overrides a function from the parent
969 // the signature must be equal.
970 char_u *pname = pf->uf_name;
971 for (int ci = 0; ci < gap->ga_len; ++ci)
972 {
973 ufunc_T *cf = (*fup)[ci];
974 char_u *cname = cf->uf_name;
975 if (STRCMP(pname, cname) == 0)
976 {
977 where_T where = WHERE_INIT;
978 where.wt_func_name = (char *)pname;
979 where.wt_kind = WT_METHOD;
980 (void)check_type(pf->uf_func_type, cf->uf_func_type,
981 TRUE, where);
982 }
983 }
984 }
985 }
986
987 *fcount -= skipped;
988
989 // Set the class pointer on all the functions and object methods.
990 for (int i = 0; i < *fcount; ++i)
991 {
992 ufunc_T *fp = (*fup)[i];
993 fp->uf_class = cl;
994 if (loop == 2)
995 fp->uf_flags |= FC_OBJECT;
996 }
997 }
998
999 return OK;
1000}
1001
1002/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001003 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001004 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001005 */
1006 void
1007ex_class(exarg_T *eap)
1008{
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001009 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1010 long start_lnum = SOURCING_LNUM;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001011
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001012 char_u *arg = eap->arg;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001013 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001014 if (is_abstract)
1015 {
1016 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1017 {
1018 semsg(_(e_invalid_argument_str), arg);
1019 return;
1020 }
1021 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001022 is_class = TRUE;
1023 }
1024
1025 if (!current_script_is_vim9()
1026 || (cmdmod.cmod_flags & CMOD_LEGACY)
1027 || !getline_equal(eap->getline, eap->cookie, getsourceline))
1028 {
1029 if (is_class)
1030 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1031 else
1032 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1033 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001034 }
1035
1036 if (!ASCII_ISUPPER(*arg))
1037 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001038 if (is_class)
1039 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1040 else
1041 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1042 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001043 return;
1044 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001045 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1046 if (!IS_WHITE_OR_NUL(*name_end))
1047 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001048 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001049 return;
1050 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001051 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001052
Bram Moolenaara86655a2023-01-12 17:06:27 +00001053 // "export class" gets used when creating the class, don't use "is_export"
1054 // for the items inside the class.
1055 int class_export = is_export;
1056 is_export = FALSE;
1057
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001058 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001059 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001060
Bram Moolenaar83677162023-01-08 19:54:10 +00001061 // Name for "extends BaseClass"
1062 char_u *extends = NULL;
1063
Bram Moolenaar94674f22023-01-06 18:42:20 +00001064 // Names for "implements SomeInterface"
1065 garray_T ga_impl;
1066 ga_init2(&ga_impl, sizeof(char_u *), 5);
1067
1068 arg = skipwhite(name_end);
1069 while (*arg != NUL && *arg != '#' && *arg != '\n')
1070 {
1071 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001072 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001073 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1074 {
1075 if (extends != NULL)
1076 {
1077 emsg(_(e_duplicate_extends));
1078 goto early_ret;
1079 }
1080 arg = skipwhite(arg + 7);
1081 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1082 if (!IS_WHITE_OR_NUL(*end))
1083 {
1084 semsg(_(e_white_space_required_after_name_str), arg);
1085 goto early_ret;
1086 }
1087 extends = vim_strnsave(arg, end - arg);
1088 if (extends == NULL)
1089 goto early_ret;
1090
1091 arg = skipwhite(end + 1);
1092 }
1093 else if (STRNCMP(arg, "implements", 10) == 0
1094 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001095 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001096 if (ga_impl.ga_len > 0)
1097 {
1098 emsg(_(e_duplicate_implements));
1099 goto early_ret;
1100 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001101 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001102
1103 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001104 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001105 char_u *impl_end = find_name_end(arg, NULL, NULL,
1106 FNE_CHECK_START);
1107 if (!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1108 {
1109 semsg(_(e_white_space_required_after_name_str), arg);
1110 goto early_ret;
1111 }
1112 char_u *iname = vim_strnsave(arg, impl_end - arg);
1113 if (iname == NULL)
1114 goto early_ret;
1115 for (int i = 0; i < ga_impl.ga_len; ++i)
1116 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1117 {
1118 semsg(_(e_duplicate_interface_after_implements_str),
1119 iname);
1120 vim_free(iname);
1121 goto early_ret;
1122 }
1123 if (ga_add_string(&ga_impl, iname) == FAIL)
1124 {
1125 vim_free(iname);
1126 goto early_ret;
1127 }
1128 if (*impl_end != ',')
1129 {
1130 arg = skipwhite(impl_end);
1131 break;
1132 }
1133 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001134 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001135 }
1136 else
1137 {
1138 semsg(_(e_trailing_characters_str), arg);
1139early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001140 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001141 ga_clear_strings(&ga_impl);
1142 return;
1143 }
1144 }
1145
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001146 garray_T type_list; // list of pointers to allocated types
1147 ga_init2(&type_list, sizeof(type_T *), 10);
1148
Bram Moolenaard505d172022-12-18 21:42:55 +00001149 // Growarray with class members declared in the class.
1150 garray_T classmembers;
1151 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1152
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001153 // Growarray with functions declared in the class.
1154 garray_T classfunctions;
1155 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001156
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001157 // Growarray with object members declared in the class.
1158 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001159 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001160
1161 // Growarray with object methods declared in the class.
1162 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001163 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001164
1165 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001166 * Go over the body of the class/interface until "endclass" or
1167 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001168 */
1169 char_u *theline = NULL;
1170 int success = FALSE;
1171 for (;;)
1172 {
1173 vim_free(theline);
1174 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
1175 if (theline == NULL)
1176 break;
1177 char_u *line = skipwhite(theline);
1178
Bram Moolenaar418b5472022-12-20 13:38:22 +00001179 // Skip empty and comment lines.
1180 if (*line == NUL)
1181 continue;
1182 if (*line == '#')
1183 {
1184 if (vim9_bad_comment(line))
1185 break;
1186 continue;
1187 }
1188
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001189 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001190 char *end_name = is_class ? "endclass" : "endinterface";
1191 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001192 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001193 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001194 semsg(_(e_command_cannot_be_shortened_str), line);
1195 else if (*p == '|' || !ends_excmd2(line, p))
1196 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001197 else
1198 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001199 break;
1200 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001201 char *wrong_name = is_class ? "endinterface" : "endclass";
1202 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1203 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001204 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001205 break;
1206 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001207
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001208 int has_public = FALSE;
1209 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001210 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001211 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001212 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001213 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001214 break;
1215 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001216 has_public = TRUE;
1217 p = skipwhite(line + 6);
1218
Bram Moolenaard505d172022-12-18 21:42:55 +00001219 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001220 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001221 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001222 break;
1223 }
1224 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001225
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001226 int has_static = FALSE;
1227 char_u *ps = p;
1228 if (checkforcmd(&p, "static", 4))
1229 {
1230 if (STRNCMP(ps, "static", 6) != 0)
1231 {
1232 semsg(_(e_command_cannot_be_shortened_str), ps);
1233 break;
1234 }
1235 has_static = TRUE;
1236 p = skipwhite(ps + 6);
1237 }
1238
Bram Moolenaard505d172022-12-18 21:42:55 +00001239 // object members (public, read access, private):
1240 // "this._varname"
1241 // "this.varname"
1242 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001243 if (STRNCMP(p, "this", 4) == 0)
1244 {
1245 if (p[4] != '.' || !eval_isnamec1(p[5]))
1246 {
1247 semsg(_(e_invalid_object_member_declaration_str), p);
1248 break;
1249 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001250 if (has_static)
1251 {
1252 emsg(_(e_static_cannot_be_followed_by_this));
1253 break;
1254 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001255 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +00001256 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001257 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001258 char_u *init_expr = NULL;
1259 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001260 &varname_end, &type_list, &type,
1261 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001262 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001263 if (is_duplicate_member(&objmembers, varname, varname_end))
1264 {
1265 vim_free(init_expr);
1266 break;
1267 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001268 if (add_member(&objmembers, varname, varname_end,
1269 has_public, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001270 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001271 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001272 break;
1273 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001274 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001275
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001276 // constructors:
1277 // def new()
1278 // enddef
1279 // def newOther()
1280 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001281 // object methods and class functions:
1282 // def SomeMethod()
1283 // enddef
1284 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001285 // enddef
1286 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001287 // def <Tval> someMethod()
1288 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001289 else if (checkforcmd(&p, "def", 3))
1290 {
1291 exarg_T ea;
1292 garray_T lines_to_free;
1293
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001294 // TODO: error for "public static def Func()"?
1295
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001296 CLEAR_FIELD(ea);
1297 ea.cmd = line;
1298 ea.arg = p;
1299 ea.cmdidx = CMD_def;
1300 ea.getline = eap->getline;
1301 ea.cookie = eap->cookie;
1302
1303 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001304 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
1305 is_class ? CF_CLASS : CF_INTERFACE);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001306 ga_clear_strings(&lines_to_free);
1307
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001308 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001309 {
Bram Moolenaar58b40092023-01-11 15:59:05 +00001310 char_u *name = uf->uf_name;
1311 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001312
1313 if (is_new && !is_valid_constructor(uf, is_abstract, has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001314 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001315 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001316 break;
1317 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001318
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001319 garray_T *fgap = has_static || is_new
1320 ? &classfunctions : &objmethods;
Bram Moolenaar58b40092023-01-11 15:59:05 +00001321 // Check the name isn't used already.
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001322 if (is_duplicate_method(fgap, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001323 {
1324 success = FALSE;
1325 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001326 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001327 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001328
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001329 if (ga_grow(fgap, 1) == OK)
1330 {
1331 if (is_new)
1332 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001333
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001334 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1335 ++fgap->ga_len;
1336 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001337 }
1338 }
1339
1340 // class members
1341 else if (has_static)
1342 {
1343 // class members (public, read access, private):
1344 // "static _varname"
1345 // "static varname"
1346 // "public static varname"
1347 char_u *varname = p;
1348 char_u *varname_end = NULL;
1349 type_T *type = NULL;
1350 char_u *init_expr = NULL;
1351 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001352 &varname_end, &type_list, &type,
1353 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001354 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001355 if (is_duplicate_member(&classmembers, varname, varname_end))
1356 {
1357 vim_free(init_expr);
1358 break;
1359 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001360 if (add_member(&classmembers, varname, varname_end,
1361 has_public, type, init_expr) == FAIL)
1362 {
1363 vim_free(init_expr);
1364 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001365 }
1366 }
1367
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001368 else
1369 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001370 if (is_class)
1371 semsg(_(e_not_valid_command_in_class_str), line);
1372 else
1373 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001374 break;
1375 }
1376 }
1377 vim_free(theline);
1378
Bram Moolenaar83677162023-01-08 19:54:10 +00001379 class_T *extends_cl = NULL; // class from "extends" argument
1380
1381 /*
1382 * Check a few things before defining the class.
1383 */
1384
1385 // Check the "extends" class is valid.
1386 if (success && extends != NULL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001387 success = validate_extends_class(extends, &extends_cl);
Bram Moolenaar83677162023-01-08 19:54:10 +00001388 VIM_CLEAR(extends);
1389
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001390 // Check the new class members and object members doesn't duplicate the
1391 // members in the extended class lineage.
1392 if (success && extends_cl != NULL)
1393 success = validate_extends_members(&classmembers, &objmembers,
1394 extends_cl);
1395
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001396 class_T **intf_classes = NULL;
1397
Bram Moolenaar83677162023-01-08 19:54:10 +00001398 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001399 if (success && ga_impl.ga_len > 0)
1400 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001401 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1402
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001403 success = validate_implements_classes(&ga_impl, intf_classes,
1404 &classfunctions, &classmembers,
1405 &objmethods, &objmembers);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001406 }
1407
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001408 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001409 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001410 success = check_func_arg_names(&classfunctions, &objmethods,
1411 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001412
Bram Moolenaareb533502022-12-14 15:06:11 +00001413 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001414 if (success)
1415 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001416 // "endclass" encountered without failures: Create the class.
1417
Bram Moolenaareb533502022-12-14 15:06:11 +00001418 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001419 if (cl == NULL)
1420 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001421 if (!is_class)
1422 cl->class_flags = CLASS_INTERFACE;
1423
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001424 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001425 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001426 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001427 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001428
Bram Moolenaard0200c82023-01-28 15:19:40 +00001429 if (extends_cl != NULL)
1430 {
1431 cl->class_extends = extends_cl;
1432 extends_cl->class_flags |= CLASS_EXTENDED;
1433 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001434
Bram Moolenaard505d172022-12-18 21:42:55 +00001435 // Add class and object members to "cl".
1436 if (add_members_to_class(&classmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001437 extends_cl == NULL ? NULL
1438 : extends_cl->class_class_members,
1439 extends_cl == NULL ? 0
1440 : extends_cl->class_class_member_count,
1441 &cl->class_class_members,
1442 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001443 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001444 extends_cl == NULL ? NULL
1445 : extends_cl->class_obj_members,
1446 extends_cl == NULL ? 0
1447 : extends_cl->class_obj_member_count,
1448 &cl->class_obj_members,
1449 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001450 goto cleanup;
1451
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001452 if (ga_impl.ga_len > 0)
1453 {
1454 // Move the "implements" names into the class.
1455 cl->class_interface_count = ga_impl.ga_len;
1456 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1457 if (cl->class_interfaces == NULL)
1458 goto cleanup;
1459 for (int i = 0; i < ga_impl.ga_len; ++i)
1460 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1461 VIM_CLEAR(ga_impl.ga_data);
1462 ga_impl.ga_len = 0;
1463
Bram Moolenaard0200c82023-01-28 15:19:40 +00001464 cl->class_interfaces_cl = intf_classes;
1465 intf_classes = NULL;
1466 }
1467
1468 if (cl->class_interface_count > 0 || extends_cl != NULL)
1469 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001470 // Add a method and member lookup table to each of the interface
1471 // classes.
1472 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1473 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001474 }
1475
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001476 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001477 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001478 add_class_members(cl, eap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001479
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001480 int have_new = FALSE;
1481 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001482 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001483 {
1484 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1485 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001486 {
1487 have_new = TRUE;
1488 break;
1489 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001490 }
1491
1492 if (have_new)
1493 // The return type of new() is an object of class "cl"
1494 class_func->uf_ret_type->tt_class = cl;
1495 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001496 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001497 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001498
Bram Moolenaar58b40092023-01-11 15:59:05 +00001499 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001500 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1501 &objmethods) == FAIL)
1502 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001503
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001504 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001505 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001506 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001507 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001508 cl->class_type_list = type_list;
1509
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001510 class_created(cl);
1511
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001512 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001513 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001514
1515 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001516 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02001517 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001518 typval_T tv;
1519 tv.v_type = VAR_CLASS;
1520 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001521 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001522 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001523 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001524 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001525 return;
1526 }
1527
1528cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00001529 if (cl != NULL)
1530 {
1531 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001532 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001533 if (cl->class_interfaces != NULL)
1534 {
1535 for (int i = 0; i < cl->class_interface_count; ++i)
1536 vim_free(cl->class_interfaces[i]);
1537 vim_free(cl->class_interfaces);
1538 }
1539 if (cl->class_interfaces_cl != NULL)
1540 {
1541 for (int i = 0; i < cl->class_interface_count; ++i)
1542 class_unref(cl->class_interfaces_cl[i]);
1543 vim_free(cl->class_interfaces_cl);
1544 }
Bram Moolenaareb533502022-12-14 15:06:11 +00001545 vim_free(cl->class_obj_members);
1546 vim_free(cl->class_obj_methods);
1547 vim_free(cl);
1548 }
1549
Bram Moolenaar83677162023-01-08 19:54:10 +00001550 vim_free(extends);
1551 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001552
1553 if (intf_classes != NULL)
1554 {
1555 for (int i = 0; i < ga_impl.ga_len; ++i)
1556 class_unref(intf_classes[i]);
1557 vim_free(intf_classes);
1558 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001559 ga_clear_strings(&ga_impl);
1560
Bram Moolenaard505d172022-12-18 21:42:55 +00001561 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001562 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001563 garray_T *gap = round == 1 ? &classmembers : &objmembers;
1564 if (gap->ga_len == 0 || gap->ga_data == NULL)
1565 continue;
1566
1567 for (int i = 0; i < gap->ga_len; ++i)
1568 {
1569 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
1570 vim_free(m->ocm_name);
1571 vim_free(m->ocm_init);
1572 }
1573 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001574 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001575
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001576 for (int i = 0; i < objmethods.ga_len; ++i)
1577 {
1578 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
1579 func_clear_free(uf, FALSE);
1580 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001581 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001582
1583 for (int i = 0; i < classfunctions.ga_len; ++i)
1584 {
1585 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
1586 func_clear_free(uf, FALSE);
1587 }
1588 ga_clear(&classfunctions);
1589
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001590 clear_type_list(&type_list);
1591}
1592
1593/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001594 * Find member "name" in class "cl", set "member_idx" to the member index and
1595 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001596 * When "is_object" is TRUE, then look for object members. Otherwise look for
1597 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001598 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02001599 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001600 */
1601 type_T *
1602class_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001603 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001604 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001605 char_u *name,
1606 char_u *name_end,
1607 int *member_idx,
Ernie Rael456ae552023-09-01 18:54:54 +02001608 ocmember_T **p_m)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001609{
1610 *member_idx = -1; // not found (yet)
1611 size_t len = name_end - name;
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001612 int member_count = is_object ? cl->class_obj_member_count
1613 : cl->class_class_member_count;
1614 ocmember_T *members = is_object ? cl->class_obj_members
1615 : cl->class_class_members;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001616
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001617 for (int i = 0; i < member_count; ++i)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001618 {
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001619 ocmember_T *m = members + i;
Bram Moolenaard505d172022-12-18 21:42:55 +00001620 if (STRNCMP(m->ocm_name, name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001621 {
1622 *member_idx = i;
Ernie Rael456ae552023-09-01 18:54:54 +02001623 if (p_m != NULL)
1624 *p_m = m;
Bram Moolenaard505d172022-12-18 21:42:55 +00001625 return m->ocm_type;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001626 }
1627 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001628
1629 semsg(_(e_unknown_variable_str), name);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001630 return &t_any;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001631}
1632
1633/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001634 * Handle ":enum" up to ":endenum".
1635 */
1636 void
1637ex_enum(exarg_T *eap UNUSED)
1638{
1639 // TODO
1640}
1641
1642/*
1643 * Handle ":type".
1644 */
1645 void
1646ex_type(exarg_T *eap UNUSED)
1647{
1648 // TODO
1649}
1650
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001651/*
1652 * Evaluate what comes after a class:
1653 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001654 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001655 * - class constructor: SomeClass.new()
1656 * - object member: someObject.varname
1657 * - object method: someObject.SomeMethod()
1658 *
1659 * "*arg" points to the '.'.
1660 * "*arg" is advanced to after the member name or method call.
1661 *
1662 * Returns FAIL or OK.
1663 */
1664 int
1665class_object_index(
1666 char_u **arg,
1667 typval_T *rettv,
1668 evalarg_T *evalarg,
1669 int verbose UNUSED) // give error messages
1670{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001671 if (VIM_ISWHITE((*arg)[1]))
1672 {
1673 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
1674 return FAIL;
1675 }
1676
1677 ++*arg;
1678 char_u *name = *arg;
1679 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
1680 if (name_end == name)
1681 return FAIL;
1682 size_t len = name_end - name;
1683
Bram Moolenaar552bdca2023-02-17 21:08:50 +00001684 class_T *cl;
1685 if (rettv->v_type == VAR_CLASS)
1686 cl = rettv->vval.v_class;
1687 else // VAR_OBJECT
1688 {
1689 if (rettv->vval.v_object == NULL)
1690 {
1691 emsg(_(e_using_null_object));
1692 return FAIL;
1693 }
1694 cl = rettv->vval.v_object->obj_class;
1695 }
1696
Bram Moolenaard13dd302023-03-11 20:56:35 +00001697 if (cl == NULL)
1698 {
1699 emsg(_(e_incomplete_type));
1700 return FAIL;
1701 }
1702
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001703 if (*name_end == '(')
1704 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001705 int on_class = rettv->v_type == VAR_CLASS;
1706 int count = on_class ? cl->class_class_function_count
1707 : cl->class_obj_method_count;
1708 for (int i = 0; i < count; ++i)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001709 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001710 ufunc_T *fp = on_class ? cl->class_class_functions[i]
1711 : cl->class_obj_methods[i];
Bram Moolenaar4ae00572022-12-09 22:49:23 +00001712 // Use a separate pointer to avoid that ASAN complains about
1713 // uf_name[] only being 4 characters.
1714 char_u *ufname = (char_u *)fp->uf_name;
1715 if (STRNCMP(name, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001716 {
1717 typval_T argvars[MAX_FUNC_ARGS + 1];
1718 int argcount = 0;
1719
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001720 if (*ufname == '_')
Yegappan Lakshmanancd7293b2023-08-27 19:18:23 +02001721 {
1722 // Cannot access a private method outside of a class
1723 semsg(_(e_cannot_access_private_method_str), name);
1724 return FAIL;
1725 }
1726
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001727 char_u *argp = name_end;
1728 int ret = get_func_arguments(&argp, evalarg, 0,
1729 argvars, &argcount);
1730 if (ret == FAIL)
1731 return FAIL;
1732
1733 funcexe_T funcexe;
1734 CLEAR_FIELD(funcexe);
1735 funcexe.fe_evaluate = TRUE;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001736 if (rettv->v_type == VAR_OBJECT)
1737 {
1738 funcexe.fe_object = rettv->vval.v_object;
1739 ++funcexe.fe_object->obj_refcount;
1740 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001741
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001742 // Clear the class or object after calling the function, in
1743 // case the refcount is one.
1744 typval_T tv_tofree = *rettv;
1745 rettv->v_type = VAR_UNKNOWN;
1746
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001747 // Call the user function. Result goes into rettv;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001748 int error = call_user_func_check(fp, argcount, argvars,
1749 rettv, &funcexe, NULL);
1750
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001751 // Clear the previous rettv and the arguments.
1752 clear_tv(&tv_tofree);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001753 for (int idx = 0; idx < argcount; ++idx)
1754 clear_tv(&argvars[idx]);
1755
1756 if (error != FCERR_NONE)
1757 {
1758 user_func_error(error, printable_func_name(fp),
1759 funcexe.fe_found_var);
1760 return FAIL;
1761 }
1762 *arg = argp;
1763 return OK;
1764 }
1765 }
1766
1767 semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name);
1768 }
1769
1770 else if (rettv->v_type == VAR_OBJECT)
1771 {
1772 for (int i = 0; i < cl->class_obj_member_count; ++i)
1773 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001774 ocmember_T *m = &cl->class_obj_members[i];
1775 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001776 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001777 if (*name == '_')
1778 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001779 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001780 return FAIL;
1781 }
1782
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001783 // The object only contains a pointer to the class, the member
1784 // values array follows right after that.
1785 object_T *obj = rettv->vval.v_object;
1786 typval_T *tv = (typval_T *)(obj + 1) + i;
1787 copy_tv(tv, rettv);
1788 object_unref(obj);
1789
1790 *arg = name_end;
1791 return OK;
1792 }
1793 }
1794
1795 semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
1796 }
1797
Bram Moolenaard505d172022-12-18 21:42:55 +00001798 else if (rettv->v_type == VAR_CLASS)
1799 {
1800 // class member
1801 for (int i = 0; i < cl->class_class_member_count; ++i)
1802 {
1803 ocmember_T *m = &cl->class_class_members[i];
1804 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
1805 {
1806 if (*name == '_')
1807 {
1808 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
1809 return FAIL;
1810 }
1811
1812 typval_T *tv = &cl->class_members_tv[i];
1813 copy_tv(tv, rettv);
1814 class_unref(cl);
1815
1816 *arg = name_end;
1817 return OK;
1818 }
1819 }
1820
1821 semsg(_(e_member_not_found_on_class_str_str), cl->class_name, name);
1822 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001823
1824 return FAIL;
1825}
1826
1827/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001828 * If "arg" points to a class or object method, return it.
1829 * Otherwise return NULL.
1830 */
1831 ufunc_T *
1832find_class_func(char_u **arg)
1833{
1834 char_u *name = *arg;
1835 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
1836 if (name_end == name || *name_end != '.')
1837 return NULL;
1838
1839 size_t len = name_end - name;
1840 typval_T tv;
1841 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00001842 if (eval_variable(name, (int)len,
1843 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001844 return NULL;
1845 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00001846 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001847
1848 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
1849 : tv.vval.v_object->obj_class;
1850 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00001851 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001852 char_u *fname = name_end + 1;
1853 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
1854 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00001855 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001856 len = fname_end - fname;
1857
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001858 int count = tv.v_type == VAR_CLASS ? cl->class_class_function_count
1859 : cl->class_obj_method_count;
1860 ufunc_T **funcs = tv.v_type == VAR_CLASS ? cl->class_class_functions
1861 : cl->class_obj_methods;
1862 for (int i = 0; i < count; ++i)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001863 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001864 ufunc_T *fp = funcs[i];
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001865 // Use a separate pointer to avoid that ASAN complains about
1866 // uf_name[] only being 4 characters.
1867 char_u *ufname = (char_u *)fp->uf_name;
1868 if (STRNCMP(fname, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaareb533502022-12-14 15:06:11 +00001869 {
1870 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001871 return fp;
Bram Moolenaareb533502022-12-14 15:06:11 +00001872 }
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001873 }
1874
Bram Moolenaareb533502022-12-14 15:06:11 +00001875fail_after_eval:
1876 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001877 return NULL;
1878}
1879
1880/*
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001881 * If "name[len]" is a class member in cctx->ctx_ufunc->uf_class return the
1882 * index in class.class_class_members[].
1883 * If "cl_ret" is not NULL set it to the class.
1884 * Otherwise return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001885 */
1886 int
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001887class_member_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001888{
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001889 if (cctx == NULL || cctx->ctx_ufunc == NULL
1890 || cctx->ctx_ufunc->uf_class == NULL)
1891 return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001892 class_T *cl = cctx->ctx_ufunc->uf_class;
1893
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001894 for (int i = 0; i < cl->class_class_member_count; ++i)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001895 {
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001896 ocmember_T *m = &cl->class_class_members[i];
1897 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001898 {
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001899 if (cl_ret != NULL)
1900 *cl_ret = cl;
1901 return i;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001902 }
1903 }
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001904 return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001905}
1906
1907/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00001908 * Return TRUE if current context "cctx_arg" is inside class "cl".
1909 * Return FALSE if not.
1910 */
1911 int
1912inside_class(cctx_T *cctx_arg, class_T *cl)
1913{
1914 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
1915 if (cctx->ctx_ufunc != NULL && cctx->ctx_ufunc->uf_class == cl)
1916 return TRUE;
1917 return FALSE;
1918}
1919
1920/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001921 * Make a copy of an object.
1922 */
1923 void
1924copy_object(typval_T *from, typval_T *to)
1925{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02001926 if (from->vval.v_object == NULL)
1927 to->vval.v_object = NULL;
1928 else
1929 {
1930 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001931 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02001932 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001933}
1934
1935/*
1936 * Free an object.
1937 */
1938 static void
1939object_clear(object_T *obj)
1940{
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01001941 // Avoid a recursive call, it can happen if "obj" has a circular reference.
1942 obj->obj_refcount = INT_MAX;
1943
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001944 class_T *cl = obj->obj_class;
1945
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02001946 if (!cl)
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02001947 return;
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02001948
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001949 // the member values are just after the object structure
1950 typval_T *tv = (typval_T *)(obj + 1);
1951 for (int i = 0; i < cl->class_obj_member_count; ++i)
1952 clear_tv(tv + i);
1953
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001954 // Remove from the list headed by "first_object".
1955 object_cleared(obj);
1956
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001957 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001958 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001959}
1960
1961/*
1962 * Unreference an object.
1963 */
1964 void
1965object_unref(object_T *obj)
1966{
1967 if (obj != NULL && --obj->obj_refcount <= 0)
1968 object_clear(obj);
1969}
1970
1971/*
1972 * Make a copy of a class.
1973 */
1974 void
1975copy_class(typval_T *from, typval_T *to)
1976{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02001977 if (from->vval.v_class == NULL)
1978 to->vval.v_class = NULL;
1979 else
1980 {
1981 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001982 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02001983 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001984}
1985
1986/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001987 * Free the class "cl" and its contents.
1988 */
1989 static void
1990class_free(class_T *cl)
1991{
1992 // Freeing what the class contains may recursively come back here.
1993 // Clear "class_name" first, if it is NULL the class does not need to
1994 // be freed.
1995 VIM_CLEAR(cl->class_name);
1996
1997 class_unref(cl->class_extends);
1998
1999 for (int i = 0; i < cl->class_interface_count; ++i)
2000 {
2001 vim_free(((char_u **)cl->class_interfaces)[i]);
2002 if (cl->class_interfaces_cl[i] != NULL)
2003 class_unref(cl->class_interfaces_cl[i]);
2004 }
2005 vim_free(cl->class_interfaces);
2006 vim_free(cl->class_interfaces_cl);
2007
2008 itf2class_T *next;
2009 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2010 {
2011 next = i2c->i2c_next;
2012 vim_free(i2c);
2013 }
2014
2015 for (int i = 0; i < cl->class_class_member_count; ++i)
2016 {
2017 ocmember_T *m = &cl->class_class_members[i];
2018 vim_free(m->ocm_name);
2019 vim_free(m->ocm_init);
2020 if (cl->class_members_tv != NULL)
2021 clear_tv(&cl->class_members_tv[i]);
2022 }
2023 vim_free(cl->class_class_members);
2024 vim_free(cl->class_members_tv);
2025
2026 for (int i = 0; i < cl->class_obj_member_count; ++i)
2027 {
2028 ocmember_T *m = &cl->class_obj_members[i];
2029 vim_free(m->ocm_name);
2030 vim_free(m->ocm_init);
2031 }
2032 vim_free(cl->class_obj_members);
2033
2034 for (int i = 0; i < cl->class_class_function_count; ++i)
2035 {
2036 ufunc_T *uf = cl->class_class_functions[i];
2037 func_clear_free(uf, FALSE);
2038 }
2039 vim_free(cl->class_class_functions);
2040
2041 for (int i = 0; i < cl->class_obj_method_count; ++i)
2042 {
2043 ufunc_T *uf = cl->class_obj_methods[i];
2044 func_clear_free(uf, FALSE);
2045 }
2046 vim_free(cl->class_obj_methods);
2047
2048 clear_type_list(&cl->class_type_list);
2049
2050 class_cleared(cl);
2051
2052 vim_free(cl);
2053}
2054
2055/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002056 * Unreference a class. Free it when the reference count goes down to zero.
2057 */
2058 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002059class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002060{
Bram Moolenaard505d172022-12-18 21:42:55 +00002061 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002062 class_free(cl);
2063}
2064
2065/*
2066 * Go through the list of all classes and free items without "copyID".
2067 */
2068 int
2069class_free_nonref(int copyID)
2070{
2071 int did_free = FALSE;
2072
2073 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002074 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002075 next_nonref_class = cl->class_next_used;
2076 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002077 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002078 // Free the class and items it contains.
2079 class_free(cl);
2080 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002081 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002082 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002083
2084 next_nonref_class = NULL;
2085 return did_free;
2086}
2087
2088 int
2089set_ref_in_classes(int copyID)
2090{
2091 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
2092 set_ref_in_item_class(cl, copyID, NULL, NULL);
2093
2094 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002095}
2096
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002097static object_T *first_object = NULL;
2098
2099/*
2100 * Call this function when an object has been created. It will be added to the
2101 * list headed by "first_object".
2102 */
2103 void
2104object_created(object_T *obj)
2105{
2106 if (first_object != NULL)
2107 {
2108 obj->obj_next_used = first_object;
2109 first_object->obj_prev_used = obj;
2110 }
2111 first_object = obj;
2112}
2113
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002114static object_T *next_nonref_obj = NULL;
2115
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002116/*
2117 * Call this function when an object has been cleared and is about to be freed.
2118 * It is removed from the list headed by "first_object".
2119 */
2120 void
2121object_cleared(object_T *obj)
2122{
2123 if (obj->obj_next_used != NULL)
2124 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
2125 if (obj->obj_prev_used != NULL)
2126 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
2127 else if (first_object == obj)
2128 first_object = obj->obj_next_used;
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002129
2130 // update the next object to check if needed
2131 if (obj == next_nonref_obj)
2132 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002133}
2134
2135/*
2136 * Go through the list of all objects and free items without "copyID".
2137 */
2138 int
2139object_free_nonref(int copyID)
2140{
2141 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002142
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002143 for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002144 {
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002145 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002146 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
2147 {
2148 // Free the object and items it contains.
2149 object_clear(obj);
2150 did_free = TRUE;
2151 }
2152 }
2153
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002154 next_nonref_obj = NULL;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002155 return did_free;
2156}
2157
LemonBoyafe04662023-08-23 21:08:11 +02002158/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002159 * Return TRUE when the class "cl", its base class or one of the implemented
2160 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02002161 */
2162 int
2163class_instance_of(class_T *cl, class_T *other_cl)
2164{
2165 if (cl == other_cl)
2166 return TRUE;
2167
2168 // Recursively check the base classes.
2169 for (; cl != NULL; cl = cl->class_extends)
2170 {
2171 if (cl == other_cl)
2172 return TRUE;
2173 // Check the implemented interfaces.
2174 for (int i = cl->class_interface_count - 1; i >= 0; --i)
2175 if (cl->class_interfaces_cl[i] == other_cl)
2176 return TRUE;
2177 }
2178
2179 return FALSE;
2180}
2181
2182/*
2183 * "instanceof(object, classinfo)" function
2184 */
2185 void
2186f_instanceof(typval_T *argvars, typval_T *rettv)
2187{
2188 typval_T *object_tv = &argvars[0];
2189 typval_T *classinfo_tv = &argvars[1];
2190 listitem_T *li;
2191
2192 rettv->vval.v_number = VVAL_FALSE;
2193
2194 if (check_for_object_arg(argvars, 0) == FAIL
2195 || check_for_class_or_list_arg(argvars, 1) == FAIL)
2196 return;
2197
2198 if (classinfo_tv->v_type == VAR_LIST)
2199 {
2200 FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
2201 {
2202 if (li->li_tv.v_type != VAR_CLASS)
2203 {
2204 emsg(_(e_class_required));
2205 return;
2206 }
2207
2208 if (class_instance_of(object_tv->vval.v_object->obj_class,
2209 li->li_tv.vval.v_class) == TRUE)
2210 {
2211 rettv->vval.v_number = VVAL_TRUE;
2212 return;
2213 }
2214 }
2215 }
2216 else if (classinfo_tv->v_type == VAR_CLASS)
2217 {
2218 rettv->vval.v_number = class_instance_of(object_tv->vval.v_object->obj_class,
2219 classinfo_tv->vval.v_class);
2220 }
2221}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002222
2223#endif // FEAT_EVAL