blob: 9328ee713c0efe0793e9f2f457461a70e1af2390 [file] [log] [blame]
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * vim9class.c: Vim9 script class support
12 */
13
14#define USING_FLOAT_STUFF
15#include "vim.h"
16
17#if defined(FEAT_EVAL) || defined(PROTO)
18
19// When not generating protos this is included in proto.h
20#ifdef PROTO
21# include "vim9.h"
22#endif
23
Yegappan Lakshmanane651e112023-09-04 07:51:01 +020024static class_T *first_class = NULL;
25static class_T *next_nonref_class = NULL;
26
27/*
28 * Call this function when a class has been created. It will be added to the
29 * list headed by "first_class".
30 */
31 static void
32class_created(class_T *cl)
33{
34 if (first_class != NULL)
35 {
36 cl->class_next_used = first_class;
37 first_class->class_prev_used = cl;
38 }
39 first_class = cl;
40}
41
42/*
43 * Call this function when a class has been cleared and is about to be freed.
44 * It is removed from the list headed by "first_class".
45 */
46 static void
47class_cleared(class_T *cl)
48{
49 if (cl->class_next_used != NULL)
50 cl->class_next_used->class_prev_used = cl->class_prev_used;
51 if (cl->class_prev_used != NULL)
52 cl->class_prev_used->class_next_used = cl->class_next_used;
53 else if (first_class == cl)
54 first_class = cl->class_next_used;
55
56 // update the next class to check if needed
57 if (cl == next_nonref_class)
58 next_nonref_class = cl->class_next_used;
59}
60
Bram Moolenaarc1c365c2022-12-04 20:13:24 +000061/*
Bram Moolenaard505d172022-12-18 21:42:55 +000062 * Parse a member declaration, both object and class member.
63 * Returns OK or FAIL. When OK then "varname_end" is set to just after the
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +020064 * variable name and "type_ret" is set to the declared or detected type.
Bram Moolenaard505d172022-12-18 21:42:55 +000065 * "init_expr" is set to the initialisation expression (allocated), if there is
Bram Moolenaar554d0312023-01-05 19:59:18 +000066 * one. For an interface "init_expr" is NULL.
Bram Moolenaard505d172022-12-18 21:42:55 +000067 */
68 static int
69parse_member(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020070 exarg_T *eap,
71 char_u *line,
72 char_u *varname,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020073 int has_public, // TRUE if "public" seen before "varname"
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020074 char_u **varname_end,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020075 garray_T *type_list,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020076 type_T **type_ret,
77 char_u **init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +000078{
79 *varname_end = to_name_end(varname, FALSE);
80 if (*varname == '_' && has_public)
81 {
82 semsg(_(e_public_member_name_cannot_start_with_underscore_str), line);
83 return FAIL;
84 }
85
86 char_u *colon = skipwhite(*varname_end);
87 char_u *type_arg = colon;
88 type_T *type = NULL;
89 if (*colon == ':')
90 {
91 if (VIM_ISWHITE(**varname_end))
92 {
93 semsg(_(e_no_white_space_allowed_before_colon_str), varname);
94 return FAIL;
95 }
96 if (!VIM_ISWHITE(colon[1]))
97 {
98 semsg(_(e_white_space_required_after_str_str), ":", varname);
99 return FAIL;
100 }
101 type_arg = skipwhite(colon + 1);
102 type = parse_type(&type_arg, type_list, TRUE);
103 if (type == NULL)
104 return FAIL;
105 }
106
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200107 char_u *init_arg = skipwhite(type_arg);
108 if (type == NULL && *init_arg != '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000109 {
110 emsg(_(e_type_or_initialization_required));
111 return FAIL;
112 }
113
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200114 if (init_expr == NULL && *init_arg == '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000115 {
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200116 emsg(_(e_cannot_initialize_member_in_interface));
117 return FAIL;
118 }
119
120 if (*init_arg == '=')
121 {
122 evalarg_T evalarg;
123 char_u *expr_start, *expr_end;
124
125 if (!VIM_ISWHITE(init_arg[-1]) || !VIM_ISWHITE(init_arg[1]))
Bram Moolenaard505d172022-12-18 21:42:55 +0000126 {
127 semsg(_(e_white_space_required_before_and_after_str_at_str),
128 "=", type_arg);
129 return FAIL;
130 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200131 init_arg = skipwhite(init_arg + 1);
Bram Moolenaard505d172022-12-18 21:42:55 +0000132
Bram Moolenaard505d172022-12-18 21:42:55 +0000133 fill_evalarg_from_eap(&evalarg, eap, FALSE);
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200134 (void)skip_expr_concatenate(&init_arg, &expr_start, &expr_end, &evalarg);
Bram Moolenaard505d172022-12-18 21:42:55 +0000135
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +0200136 // No type specified for the member. Set it to "any" and the correct
137 // type will be set when the object is instantiated.
Bram Moolenaard505d172022-12-18 21:42:55 +0000138 if (type == NULL)
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200139 type = &t_any;
Bram Moolenaard505d172022-12-18 21:42:55 +0000140
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200141 *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
142 // Free the memory pointed by expr_start.
Bram Moolenaard505d172022-12-18 21:42:55 +0000143 clear_evalarg(&evalarg, NULL);
144 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200145 else if (!valid_declaration_type(type))
Bram Moolenaard505d172022-12-18 21:42:55 +0000146 return FAIL;
147
148 *type_ret = type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000149 return OK;
150}
151
152/*
153 * Add a member to an object or a class.
154 * Returns OK when successful, "init_expr" will be consumed then.
155 * Returns FAIL otherwise, caller might need to free "init_expr".
156 */
157 static int
158add_member(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200159 garray_T *gap,
160 char_u *varname,
161 char_u *varname_end,
162 int has_public,
163 type_T *type,
164 char_u *init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +0000165{
166 if (ga_grow(gap, 1) == FAIL)
167 return FAIL;
168 ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
169 m->ocm_name = vim_strnsave(varname, varname_end - varname);
=?UTF-8?q?Ola=20S=C3=B6der?=d8742472023-03-05 13:12:32 +0000170 m->ocm_access = has_public ? VIM_ACCESS_ALL
171 : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
Bram Moolenaard505d172022-12-18 21:42:55 +0000172 m->ocm_type = type;
173 if (init_expr != NULL)
174 m->ocm_init = init_expr;
175 ++gap->ga_len;
176 return OK;
177}
178
179/*
180 * Move the class or object members found while parsing a class into the class.
181 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000182 * "parent_members" points to the members in the parent class (if any)
183 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000184 * "members" will be set to the newly allocated array of members and
185 * "member_count" set to the number of members.
186 * Returns OK or FAIL.
187 */
188 static int
189add_members_to_class(
190 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000191 ocmember_T *parent_members,
192 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000193 ocmember_T **members,
194 int *member_count)
195{
Bram Moolenaar83677162023-01-08 19:54:10 +0000196 *member_count = parent_count + gap->ga_len;
197 *members = *member_count == 0 ? NULL
198 : ALLOC_MULT(ocmember_T, *member_count);
199 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000200 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000201 for (int i = 0; i < parent_count; ++i)
202 {
203 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000204 ocmember_T *m = *members + i;
205 *m = parent_members[i];
206 m->ocm_name = vim_strsave(m->ocm_name);
207 if (m->ocm_init != NULL)
208 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000209 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000210 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000211 // new members are moved
212 mch_memmove(*members + parent_count,
213 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000214 VIM_CLEAR(gap->ga_data);
215 return OK;
216}
217
218/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000219 * Convert a member index "idx" of interface "itf" to the member index of class
220 * "cl" implementing that interface.
221 */
222 int
Ernie Rael18143d32023-09-04 22:30:41 +0200223object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl,
224 int is_static)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000225{
Ernie Rael18143d32023-09-04 22:30:41 +0200226 if (idx >= (is_method ? itf->class_obj_method_count
227 : is_static ? itf->class_class_member_count
Bram Moolenaard0200c82023-01-28 15:19:40 +0000228 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000229 {
230 siemsg("index %d out of range for interface %s", idx, itf->class_name);
231 return 0;
232 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200233
234 // If "cl" is the interface or the class that is extended, then the method
235 // index can be used directly and there is no need to search for the method
236 // index in one of the child classes.
237 if (cl == itf)
238 return idx;
239
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200240 itf2class_T *i2c = NULL;
241 int searching = TRUE;
242 int method_offset = 0;
243
Ernie Raelcf138d42023-09-06 20:45:03 +0200244 for (class_T *super = cl; super != NULL && searching;
245 super = super->class_extends)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200246 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200247 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200248 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200249 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
250 {
251 searching = FALSE;
252 break;
253 }
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200254 }
255 if (searching && is_method)
256 // The parent class methods are stored after the current class
257 // methods.
258 method_offset += is_static
259 ? super->class_class_function_count_child
260 : super->class_obj_method_count_child;
261 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000262 if (i2c == NULL)
263 {
264 siemsg("class %s not found on interface %s",
265 cl->class_name, itf->class_name);
266 return 0;
267 }
Ernie Rael18143d32023-09-04 22:30:41 +0200268 if (is_static)
269 {
270 // TODO: Need a table for fast lookup?
271 char_u *name = itf->class_class_members[idx].ocm_name;
272 for (int i = 0; i < i2c->i2c_class->class_class_member_count; ++i)
273 {
274 ocmember_T *m = &i2c->i2c_class->class_class_members[i];
275 if (STRCMP(name, m->ocm_name) == 0)
276 {
277 return i;
278 }
279 }
280 siemsg("class %s, interface %s, static %s not found",
281 cl->class_name, itf->class_name, name);
282 return 0;
283 }
284 else
285 {
286 // A table follows the i2c for the class
287 int *table = (int *)(i2c + 1);
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200288 // "method_offset" is 0, if method is in the current class. If method
289 // is in a parent class, then it is non-zero.
290 return table[idx] + method_offset;
Ernie Rael18143d32023-09-04 22:30:41 +0200291 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000292}
293
294/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200295 * Check whether a class named "extends_name" is present. If the class is
296 * valid, then "extends_clp" is set with the class pointer.
297 * Returns TRUE if the class name "extends_names" is a valid class.
298 */
299 static int
300validate_extends_class(char_u *extends_name, class_T **extends_clp)
301{
302 typval_T tv;
303 int success = FALSE;
304
305 tv.v_type = VAR_UNKNOWN;
306 if (eval_variable_import(extends_name, &tv) == FAIL)
307 {
308 semsg(_(e_class_name_not_found_str), extends_name);
309 return success;
310 }
311 else
312 {
313 if (tv.v_type != VAR_CLASS
314 || tv.vval.v_class == NULL
315 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
316 semsg(_(e_cannot_extend_str), extends_name);
317 else
318 {
319 class_T *extends_cl = tv.vval.v_class;
320 ++extends_cl->class_refcount;
321 *extends_clp = extends_cl;
322 success = TRUE;
323 }
324 clear_tv(&tv);
325 }
326
327 return success;
328}
329
330/*
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200331 * Check whether a class/object member variable in "classmembers_gap" /
332 * "objmembers_gap" is a duplicate of a member in any of the extended parent
333 * class lineage. Returns TRUE if there are no duplicates.
334 */
335 static int
336validate_extends_members(
337 garray_T *classmembers_gap,
338 garray_T *objmembers_gap,
339 class_T *extends_cl)
340{
341 for (int loop = 1; loop <= 2; ++loop)
342 {
343 // loop == 1: check class members
344 // loop == 2: check object members
345 int member_count = loop == 1 ? classmembers_gap->ga_len
346 : objmembers_gap->ga_len;
347 if (member_count == 0)
348 continue;
349 ocmember_T *members = (ocmember_T *)(loop == 1
350 ? classmembers_gap->ga_data
351 : objmembers_gap->ga_data);
352
353 // Validate each member variable
354 for (int c_i = 0; c_i < member_count; c_i++)
355 {
356 class_T *p_cl = extends_cl;
357 ocmember_T *c_m = members + c_i;
358 char_u *pstr = (*c_m->ocm_name == '_')
359 ? c_m->ocm_name + 1 : c_m->ocm_name;
360
361 // Check in all the parent classes in the lineage
362 while (p_cl != NULL)
363 {
364 int p_member_count = loop == 1
365 ? p_cl->class_class_member_count
366 : p_cl->class_obj_member_count;
367 if (p_member_count == 0)
368 continue;
369 ocmember_T *p_members = (loop == 1
370 ? p_cl->class_class_members
371 : p_cl->class_obj_members);
372
373 // Compare against all the members in the parent class
374 for (int p_i = 0; p_i < p_member_count; p_i++)
375 {
376 ocmember_T *p_m = p_members + p_i;
377 char_u *qstr = (*p_m->ocm_name == '_')
378 ? p_m->ocm_name + 1 : p_m->ocm_name;
379 if (STRCMP(pstr, qstr) == 0)
380 {
381 semsg(_(e_duplicate_member_str), c_m->ocm_name);
382 return FALSE;
383 }
384 }
385
386 p_cl = p_cl->class_extends;
387 }
388 }
389 }
390
391 return TRUE;
392}
393
394/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200395 * Check the members of the interface class "ifcl" match the class members
396 * ("classmembers_gap") and object members ("objmembers_gap") of a class.
397 * Returns TRUE if the class and object member names are valid.
398 */
399 static int
400validate_interface_members(
401 char_u *intf_class_name,
402 class_T *ifcl,
403 garray_T *classmembers_gap,
404 garray_T *objmembers_gap)
405{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200406 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200407 {
408 // loop == 1: check class members
409 // loop == 2: check object members
410 int if_count = loop == 1 ? ifcl->class_class_member_count
411 : ifcl->class_obj_member_count;
412 if (if_count == 0)
413 continue;
414 ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
415 : ifcl->class_obj_members;
416 ocmember_T *cl_ms = (ocmember_T *)(loop == 1
417 ? classmembers_gap->ga_data
418 : objmembers_gap->ga_data);
419 int cl_count = loop == 1 ? classmembers_gap->ga_len
420 : objmembers_gap->ga_len;
421 for (int if_i = 0; if_i < if_count; ++if_i)
422 {
423 int cl_i;
424 for (cl_i = 0; cl_i < cl_count; ++cl_i)
425 {
426 ocmember_T *m = &cl_ms[cl_i];
427 where_T where = WHERE_INIT;
428
429 if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) != 0)
430 continue;
431
432 // Ensure the type is matching.
433 where.wt_func_name = (char *)m->ocm_name;
434 where.wt_kind = WT_MEMBER;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200435 if (check_type(if_ms[if_i].ocm_type, m->ocm_type, TRUE,
436 where) == FAIL)
437 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200438
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200439 if (if_ms[if_i].ocm_access != m->ocm_access)
440 {
441 semsg(_(e_member_str_of_interface_str_has_different_access),
442 if_ms[if_i].ocm_name, intf_class_name);
443 return FALSE;
444 }
445
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200446 break;
447 }
448 if (cl_i == cl_count)
449 {
450 semsg(_(e_member_str_of_interface_str_not_implemented),
451 if_ms[if_i].ocm_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200452 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200453 }
454 }
455 }
456
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200457 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200458}
459
460/*
461 * Check the functions/methods of the interface class "ifcl" match the class
462 * methods ("classfunctions_gap") and object functions ("objmemthods_gap") of a
463 * class.
464 * Returns TRUE if the class and object member names are valid.
465 */
466 static int
467validate_interface_methods(
468 char_u *intf_class_name,
469 class_T *ifcl,
470 garray_T *classfunctions_gap,
471 garray_T *objmethods_gap)
472{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200473 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200474 {
475 // loop == 1: check class functions
476 // loop == 2: check object methods
477 int if_count = loop == 1 ? ifcl->class_class_function_count
478 : ifcl->class_obj_method_count;
479 if (if_count == 0)
480 continue;
481 ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
482 : ifcl->class_obj_methods;
483 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
484 ? classfunctions_gap->ga_data
485 : objmethods_gap->ga_data);
486 int cl_count = loop == 1 ? classfunctions_gap->ga_len
487 : objmethods_gap->ga_len;
488 for (int if_i = 0; if_i < if_count; ++if_i)
489 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200490 char_u *if_name = if_fp[if_i]->uf_name;
491 int cl_i;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200492 for (cl_i = 0; cl_i < cl_count; ++cl_i)
493 {
494 char_u *cl_name = cl_fp[cl_i]->uf_name;
495 if (STRCMP(if_name, cl_name) == 0)
496 {
497 where_T where = WHERE_INIT;
498
499 // Ensure the type is matching.
500 where.wt_func_name = (char *)if_name;
501 where.wt_kind = WT_METHOD;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200502 if (check_type(if_fp[if_i]->uf_func_type,
503 cl_fp[cl_i]->uf_func_type, TRUE, where) == FAIL)
504 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200505 break;
506 }
507 }
508 if (cl_i == cl_count)
509 {
510 semsg(_(e_function_str_of_interface_str_not_implemented),
511 if_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200512 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200513 }
514 }
515 }
516
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200517 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200518}
519
520/*
521 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200522 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200523 * object methods and object members in the new class are in
524 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
525 * "objmembers_gap" respectively.
526 */
527 static int
528validate_implements_classes(
529 garray_T *impl_gap,
530 class_T **intf_classes,
531 garray_T *classfunctions_gap,
532 garray_T *classmembers_gap,
533 garray_T *objmethods_gap,
534 garray_T *objmembers_gap)
535{
536 int success = TRUE;
537
538 for (int i = 0; i < impl_gap->ga_len && success; ++i)
539 {
540 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
541 typval_T tv;
542 tv.v_type = VAR_UNKNOWN;
543 if (eval_variable_import(impl, &tv) == FAIL)
544 {
545 semsg(_(e_interface_name_not_found_str), impl);
546 success = FALSE;
547 break;
548 }
549
550 if (tv.v_type != VAR_CLASS
551 || tv.vval.v_class == NULL
552 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
553 {
554 semsg(_(e_not_valid_interface_str), impl);
555 success = FALSE;
556 clear_tv(&tv);
557 break;
558 }
559
560 class_T *ifcl = tv.vval.v_class;
561 intf_classes[i] = ifcl;
562 ++ifcl->class_refcount;
563
564 // check the members of the interface match the members of the class
565 success = validate_interface_members(impl, ifcl, classmembers_gap,
566 objmembers_gap);
567
568 // check the functions/methods of the interface match the
569 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200570 if (success)
571 success = validate_interface_methods(impl, ifcl,
572 classfunctions_gap, objmethods_gap);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200573 clear_tv(&tv);
574 }
575
576 return success;
577}
578
579/*
580 * Check no function argument name is used as a class member.
581 * (Object members are always accessed with "this." prefix, so no need
582 * to check them.)
583 */
584 static int
585check_func_arg_names(
586 garray_T *classfunctions_gap,
587 garray_T *objmethods_gap,
588 garray_T *classmembers_gap)
589{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200590 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200591 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200592 {
593 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
594
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200595 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200596 {
597 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
598
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200599 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200600 {
601 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
602 garray_T *mgap = classmembers_gap;
603
604 // Check all the class member names
605 for (int mi = 0; mi < mgap->ga_len; ++mi)
606 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200607 char_u *mname =
608 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200609 if (STRCMP(aname, mname) == 0)
610 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200611 if (uf->uf_script_ctx.sc_sid > 0)
612 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
613
614 semsg(_(e_argument_already_declared_in_class_str),
615 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200616
617 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200618 }
619 }
620 }
621 }
622 }
623
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200624 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200625}
626
627/*
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200628 * Returns TRUE if the member "varname" is already defined.
629 */
630 static int
631is_duplicate_member(garray_T *mgap, char_u *varname, char_u *varname_end)
632{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200633 char_u *name = vim_strnsave(varname, varname_end - varname);
634 char_u *pstr = (*name == '_') ? name + 1 : name;
635 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200636
637 for (int i = 0; i < mgap->ga_len; ++i)
638 {
639 ocmember_T *m = ((ocmember_T *)mgap->ga_data) + i;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200640 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1 : m->ocm_name;
641 if (STRCMP(pstr, qstr) == 0)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200642 {
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200643 semsg(_(e_duplicate_member_str), name);
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200644 dup = TRUE;
645 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200646 }
647 }
648
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200649 vim_free(name);
650 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200651}
652
653/*
654 * Returns TRUE if the method "name" is already defined.
655 */
656 static int
657is_duplicate_method(garray_T *fgap, char_u *name)
658{
659 char_u *pstr = (*name == '_') ? name + 1 : name;
660
661 for (int i = 0; i < fgap->ga_len; ++i)
662 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200663 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
664 char_u *qstr = *n == '_' ? n + 1 : n;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200665 if (STRCMP(pstr, qstr) == 0)
666 {
667 semsg(_(e_duplicate_function_str), name);
668 return TRUE;
669 }
670 }
671
672 return FALSE;
673}
674
675/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200676 * Returns TRUE if the constructor is valid.
677 */
678 static int
679is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
680{
681 // Constructors are not allowed in abstract classes.
682 if (is_abstract)
683 {
684 emsg(_(e_cannot_define_new_function_in_abstract_class));
685 return FALSE;
686 }
687 // A constructor is always static, no need to define it so.
688 if (has_static)
689 {
690 emsg(_(e_cannot_define_new_function_as_static));
691 return FALSE;
692 }
693 // A return type should not be specified for the new()
694 // constructor method.
695 if (uf->uf_ret_type->tt_type != VAR_VOID)
696 {
697 emsg(_(e_cannot_use_a_return_type_with_new));
698 return FALSE;
699 }
700 return TRUE;
701}
702
703/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200704 * Update the interface class lookup table for the member index on the
705 * interface to the member index in the class implementing the interface.
706 * And a lookup table for the object method index on the interface
707 * to the object method index in the class implementing the interface.
708 * This is also used for updating the lookup table for the extended class
709 * hierarchy.
710 */
711 static int
712update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200713 class_T *ifcl,
714 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200715 garray_T *objmethods,
716 int pobj_method_offset,
717 int is_interface)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200718{
719 if (ifcl == NULL)
720 return OK;
721
722 // Table for members.
723 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200724 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200725 if (if2cl == NULL)
726 return FAIL;
727 if2cl->i2c_next = ifcl->class_itf2class;
728 ifcl->class_itf2class = if2cl;
729 if2cl->i2c_class = cl;
730 if2cl->i2c_is_method = FALSE;
731
732 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
733 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
734 {
735 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200736 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200737 {
738 int *table = (int *)(if2cl + 1);
739 table[if_i] = cl_i;
740 break;
741 }
742 }
743
744 // Table for methods.
745 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200746 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200747 if (if2cl == NULL)
748 return FAIL;
749 if2cl->i2c_next = ifcl->class_itf2class;
750 ifcl->class_itf2class = if2cl;
751 if2cl->i2c_class = cl;
752 if2cl->i2c_is_method = TRUE;
753
754 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
755 {
756 int done = FALSE;
757 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
758 {
759 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200760 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200761 {
762 int *table = (int *)(if2cl + 1);
763 table[if_i] = cl_i;
764 done = TRUE;
765 break;
766 }
767 }
768
769 // extended class object method is not overridden by the child class.
770 // Keep the method declared in one of the parent classes in the
771 // lineage.
772 if (!done && !is_interface)
773 {
774 // If "ifcl" is not the immediate parent of "cl", then search in
775 // the intermediate parent classes.
776 if (cl->class_extends != ifcl)
777 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200778 class_T *parent = cl->class_extends;
779 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200780
781 while (!done && parent != NULL && parent != ifcl)
782 {
783
784 for (int cl_i = 0;
785 cl_i < parent->class_obj_method_count_child; ++cl_i)
786 {
787 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
788 parent->class_obj_methods[cl_i]->uf_name)
789 == 0)
790 {
791 int *table = (int *)(if2cl + 1);
792 table[if_i] = method_offset + cl_i;
793 done = TRUE;
794 break;
795 }
796 }
797 method_offset += parent->class_obj_method_count_child;
798 parent = parent->class_extends;
799 }
800 }
801
802 if (!done)
803 {
804 int *table = (int *)(if2cl + 1);
805 table[if_i] = pobj_method_offset + if_i;
806 }
807 }
808 }
809
810 return OK;
811}
812
813/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200814 * Update the member and object method lookup tables for a new class in the
815 * interface class.
816 * For each interface add a lookup table for the member index on the interface
817 * to the member index in the new class. And a lookup table for the object
818 * method index on the interface to the object method index in the new class.
819 */
820 static int
821add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
822{
823 for (int i = 0; i < cl->class_interface_count; ++i)
824 {
825 class_T *ifcl = cl->class_interfaces_cl[i];
826
827 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
828 0, TRUE) == FAIL)
829 return FAIL;
830 }
831
832 // Update the lookup table for the extended class, if nay
833 if (extends_cl != NULL)
834 {
835 class_T *pclass = extends_cl;
836 int pobj_method_offset = objmethods_gap->ga_len;
837
838 // Update the entire lineage of extended classes.
839 while (pclass != NULL)
840 {
841 if (update_member_method_lookup_table(pclass, cl,
842 objmethods_gap, pobj_method_offset, FALSE) == FAIL)
843 return FAIL;
844
845 pobj_method_offset += pclass->class_obj_method_count_child;
846 pclass = pclass->class_extends;
847 }
848 }
849
850 return OK;
851}
852
853/*
854 * Add class members to a new class. Allocate a typval for each class member
855 * and initialize it.
856 */
857 static void
858add_class_members(class_T *cl, exarg_T *eap)
859{
860 // Allocate a typval for each class member and initialize it.
861 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
862 cl->class_class_member_count);
863 if (cl->class_members_tv == NULL)
864 return;
865
866 for (int i = 0; i < cl->class_class_member_count; ++i)
867 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200868 ocmember_T *m = &cl->class_class_members[i];
869 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200870 if (m->ocm_init != NULL)
871 {
872 typval_T *etv = eval_expr(m->ocm_init, eap);
873 if (etv != NULL)
874 {
875 *tv = *etv;
876 vim_free(etv);
877 }
878 }
879 else
880 {
881 // TODO: proper default value
882 tv->v_type = m->ocm_type->tt_type;
883 tv->vval.v_string = NULL;
884 }
885 }
886}
887
888/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +0200889 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200890 */
891 static void
892add_default_constructor(
893 class_T *cl,
894 garray_T *classfunctions_gap,
895 garray_T *type_list_gap)
896{
897 garray_T fga;
898
899 ga_init2(&fga, 1, 1000);
900 ga_concat(&fga, (char_u *)"new(");
901 for (int i = 0; i < cl->class_obj_member_count; ++i)
902 {
903 if (i > 0)
904 ga_concat(&fga, (char_u *)", ");
905 ga_concat(&fga, (char_u *)"this.");
906 ocmember_T *m = cl->class_obj_members + i;
907 ga_concat(&fga, (char_u *)m->ocm_name);
908 ga_concat(&fga, (char_u *)" = v:none");
909 }
910 ga_concat(&fga, (char_u *)")\nenddef\n");
911 ga_append(&fga, NUL);
912
913 exarg_T fea;
914 CLEAR_FIELD(fea);
915 fea.cmdidx = CMD_def;
916 fea.cmd = fea.arg = fga.ga_data;
917
918 garray_T lines_to_free;
919 ga_init2(&lines_to_free, sizeof(char_u *), 50);
920
921 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS);
922
923 ga_clear_strings(&lines_to_free);
924 vim_free(fga.ga_data);
925
926 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
927 {
928 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200929 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200930 ++classfunctions_gap->ga_len;
931
932 nf->uf_flags |= FC_NEW;
933 nf->uf_ret_type = get_type_ptr(type_list_gap);
934 if (nf->uf_ret_type != NULL)
935 {
936 nf->uf_ret_type->tt_type = VAR_OBJECT;
937 nf->uf_ret_type->tt_class = cl;
938 nf->uf_ret_type->tt_argcount = 0;
939 nf->uf_ret_type->tt_args = NULL;
940 }
941 }
942}
943
944/*
945 * Add the class functions and object methods to the new class "cl".
946 * When extending a class, add the functions and methods from the parent class
947 * also.
948 */
949 static int
950add_classfuncs_objmethods(
951 class_T *cl,
952 class_T *extends_cl,
953 garray_T *classfunctions_gap,
954 garray_T *objmethods_gap)
955{
956 // loop 1: class functions, loop 2: object methods
957 for (int loop = 1; loop <= 2; ++loop)
958 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200959 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
960 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200961 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200962 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200963 : &cl->class_obj_methods;
964
965 int parent_count = 0;
966 if (extends_cl != NULL)
967 // Include functions from the parent.
968 parent_count = loop == 1
969 ? extends_cl->class_class_function_count
970 : extends_cl->class_obj_method_count;
971
972 *fcount = parent_count + gap->ga_len;
973 if (*fcount == 0)
974 {
975 *fup = NULL;
976 continue;
977 }
978 *fup = ALLOC_MULT(ufunc_T *, *fcount);
979 if (*fup == NULL)
980 return FAIL;
981
982 if (gap->ga_len != 0)
983 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
984 vim_free(gap->ga_data);
985 if (loop == 1)
986 cl->class_class_function_count_child = gap->ga_len;
987 else
988 cl->class_obj_method_count_child = gap->ga_len;
989
990 int skipped = 0;
991 for (int i = 0; i < parent_count; ++i)
992 {
993 // Copy functions from the parent. Can't use the same
994 // function, because "uf_class" is different and compilation
995 // will have a different result.
996 // Put them after the functions in the current class, object
997 // methods may be overruled, then "super.Method()" is used to
998 // find a method from the parent.
999 // Skip "new" functions. TODO: not all of them.
1000 if (loop == 1 && STRNCMP(
1001 extends_cl->class_class_functions[i]->uf_name,
1002 "new", 3) == 0)
1003 ++skipped;
1004 else
1005 {
1006 ufunc_T *pf = (loop == 1
1007 ? extends_cl->class_class_functions
1008 : extends_cl->class_obj_methods)[i];
1009 (*fup)[gap->ga_len + i - skipped] = copy_function(pf);
1010
1011 // If the child class overrides a function from the parent
1012 // the signature must be equal.
1013 char_u *pname = pf->uf_name;
1014 for (int ci = 0; ci < gap->ga_len; ++ci)
1015 {
1016 ufunc_T *cf = (*fup)[ci];
1017 char_u *cname = cf->uf_name;
1018 if (STRCMP(pname, cname) == 0)
1019 {
1020 where_T where = WHERE_INIT;
1021 where.wt_func_name = (char *)pname;
1022 where.wt_kind = WT_METHOD;
1023 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1024 TRUE, where);
1025 }
1026 }
1027 }
1028 }
1029
1030 *fcount -= skipped;
1031
1032 // Set the class pointer on all the functions and object methods.
1033 for (int i = 0; i < *fcount; ++i)
1034 {
1035 ufunc_T *fp = (*fup)[i];
1036 fp->uf_class = cl;
1037 if (loop == 2)
1038 fp->uf_flags |= FC_OBJECT;
1039 }
1040 }
1041
1042 return OK;
1043}
1044
1045/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001046 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001047 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001048 */
1049 void
1050ex_class(exarg_T *eap)
1051{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001052 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1053 long start_lnum = SOURCING_LNUM;
1054 char_u *arg = eap->arg;
1055 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001056
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001057 if (is_abstract)
1058 {
1059 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1060 {
1061 semsg(_(e_invalid_argument_str), arg);
1062 return;
1063 }
1064 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001065 is_class = TRUE;
1066 }
1067
1068 if (!current_script_is_vim9()
1069 || (cmdmod.cmod_flags & CMOD_LEGACY)
1070 || !getline_equal(eap->getline, eap->cookie, getsourceline))
1071 {
1072 if (is_class)
1073 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1074 else
1075 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1076 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001077 }
1078
1079 if (!ASCII_ISUPPER(*arg))
1080 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001081 if (is_class)
1082 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1083 else
1084 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1085 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001086 return;
1087 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001088 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1089 if (!IS_WHITE_OR_NUL(*name_end))
1090 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001091 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001092 return;
1093 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001094 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001095
Bram Moolenaara86655a2023-01-12 17:06:27 +00001096 // "export class" gets used when creating the class, don't use "is_export"
1097 // for the items inside the class.
1098 int class_export = is_export;
1099 is_export = FALSE;
1100
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001101 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001102 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001103
Bram Moolenaar83677162023-01-08 19:54:10 +00001104 // Name for "extends BaseClass"
1105 char_u *extends = NULL;
1106
Bram Moolenaar94674f22023-01-06 18:42:20 +00001107 // Names for "implements SomeInterface"
1108 garray_T ga_impl;
1109 ga_init2(&ga_impl, sizeof(char_u *), 5);
1110
1111 arg = skipwhite(name_end);
1112 while (*arg != NUL && *arg != '#' && *arg != '\n')
1113 {
1114 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001115 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001116 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1117 {
1118 if (extends != NULL)
1119 {
1120 emsg(_(e_duplicate_extends));
1121 goto early_ret;
1122 }
1123 arg = skipwhite(arg + 7);
1124 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1125 if (!IS_WHITE_OR_NUL(*end))
1126 {
1127 semsg(_(e_white_space_required_after_name_str), arg);
1128 goto early_ret;
1129 }
1130 extends = vim_strnsave(arg, end - arg);
1131 if (extends == NULL)
1132 goto early_ret;
1133
1134 arg = skipwhite(end + 1);
1135 }
1136 else if (STRNCMP(arg, "implements", 10) == 0
1137 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001138 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001139 if (ga_impl.ga_len > 0)
1140 {
1141 emsg(_(e_duplicate_implements));
1142 goto early_ret;
1143 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001144 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001145
1146 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001147 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001148 char_u *impl_end = find_name_end(arg, NULL, NULL,
1149 FNE_CHECK_START);
1150 if (!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1151 {
1152 semsg(_(e_white_space_required_after_name_str), arg);
1153 goto early_ret;
1154 }
1155 char_u *iname = vim_strnsave(arg, impl_end - arg);
1156 if (iname == NULL)
1157 goto early_ret;
1158 for (int i = 0; i < ga_impl.ga_len; ++i)
1159 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1160 {
1161 semsg(_(e_duplicate_interface_after_implements_str),
1162 iname);
1163 vim_free(iname);
1164 goto early_ret;
1165 }
1166 if (ga_add_string(&ga_impl, iname) == FAIL)
1167 {
1168 vim_free(iname);
1169 goto early_ret;
1170 }
1171 if (*impl_end != ',')
1172 {
1173 arg = skipwhite(impl_end);
1174 break;
1175 }
1176 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001177 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001178 }
1179 else
1180 {
1181 semsg(_(e_trailing_characters_str), arg);
1182early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001183 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001184 ga_clear_strings(&ga_impl);
1185 return;
1186 }
1187 }
1188
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001189 garray_T type_list; // list of pointers to allocated types
1190 ga_init2(&type_list, sizeof(type_T *), 10);
1191
Bram Moolenaard505d172022-12-18 21:42:55 +00001192 // Growarray with class members declared in the class.
1193 garray_T classmembers;
1194 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1195
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001196 // Growarray with functions declared in the class.
1197 garray_T classfunctions;
1198 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001199
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001200 // Growarray with object members declared in the class.
1201 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001202 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001203
1204 // Growarray with object methods declared in the class.
1205 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001206 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001207
1208 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001209 * Go over the body of the class/interface until "endclass" or
1210 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001211 */
1212 char_u *theline = NULL;
1213 int success = FALSE;
1214 for (;;)
1215 {
1216 vim_free(theline);
1217 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
1218 if (theline == NULL)
1219 break;
1220 char_u *line = skipwhite(theline);
1221
Bram Moolenaar418b5472022-12-20 13:38:22 +00001222 // Skip empty and comment lines.
1223 if (*line == NUL)
1224 continue;
1225 if (*line == '#')
1226 {
1227 if (vim9_bad_comment(line))
1228 break;
1229 continue;
1230 }
1231
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001232 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001233 char *end_name = is_class ? "endclass" : "endinterface";
1234 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001235 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001236 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001237 semsg(_(e_command_cannot_be_shortened_str), line);
1238 else if (*p == '|' || !ends_excmd2(line, p))
1239 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001240 else
1241 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001242 break;
1243 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001244 char *wrong_name = is_class ? "endinterface" : "endclass";
1245 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1246 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001247 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001248 break;
1249 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001250
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001251 int has_public = FALSE;
1252 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001253 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001254 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001255 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001256 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001257 break;
1258 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001259 has_public = TRUE;
1260 p = skipwhite(line + 6);
1261
Bram Moolenaard505d172022-12-18 21:42:55 +00001262 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001263 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001264 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001265 break;
1266 }
1267 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001268
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001269 int has_static = FALSE;
1270 char_u *ps = p;
1271 if (checkforcmd(&p, "static", 4))
1272 {
1273 if (STRNCMP(ps, "static", 6) != 0)
1274 {
1275 semsg(_(e_command_cannot_be_shortened_str), ps);
1276 break;
1277 }
1278 has_static = TRUE;
1279 p = skipwhite(ps + 6);
1280 }
1281
Bram Moolenaard505d172022-12-18 21:42:55 +00001282 // object members (public, read access, private):
1283 // "this._varname"
1284 // "this.varname"
1285 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001286 if (STRNCMP(p, "this", 4) == 0)
1287 {
1288 if (p[4] != '.' || !eval_isnamec1(p[5]))
1289 {
1290 semsg(_(e_invalid_object_member_declaration_str), p);
1291 break;
1292 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001293 if (has_static)
1294 {
1295 emsg(_(e_static_cannot_be_followed_by_this));
1296 break;
1297 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001298 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +00001299 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001300 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001301 char_u *init_expr = NULL;
1302 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001303 &varname_end, &type_list, &type,
1304 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001305 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001306 if (is_duplicate_member(&objmembers, varname, varname_end))
1307 {
1308 vim_free(init_expr);
1309 break;
1310 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001311 if (add_member(&objmembers, varname, varname_end,
1312 has_public, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001313 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001314 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001315 break;
1316 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001317 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001318
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001319 // constructors:
1320 // def new()
1321 // enddef
1322 // def newOther()
1323 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001324 // object methods and class functions:
1325 // def SomeMethod()
1326 // enddef
1327 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001328 // enddef
1329 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001330 // def <Tval> someMethod()
1331 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001332 else if (checkforcmd(&p, "def", 3))
1333 {
1334 exarg_T ea;
1335 garray_T lines_to_free;
1336
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001337 // TODO: error for "public static def Func()"?
1338
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001339 CLEAR_FIELD(ea);
1340 ea.cmd = line;
1341 ea.arg = p;
1342 ea.cmdidx = CMD_def;
1343 ea.getline = eap->getline;
1344 ea.cookie = eap->cookie;
1345
1346 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001347 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
1348 is_class ? CF_CLASS : CF_INTERFACE);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001349 ga_clear_strings(&lines_to_free);
1350
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001351 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001352 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001353 char_u *name = uf->uf_name;
1354 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001355
1356 if (is_new && !is_valid_constructor(uf, is_abstract, has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001357 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001358 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001359 break;
1360 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001361
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001362 garray_T *fgap = has_static || is_new
1363 ? &classfunctions : &objmethods;
Bram Moolenaar58b40092023-01-11 15:59:05 +00001364 // Check the name isn't used already.
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001365 if (is_duplicate_method(fgap, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001366 {
1367 success = FALSE;
1368 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001369 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001370 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001371
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001372 if (ga_grow(fgap, 1) == OK)
1373 {
1374 if (is_new)
1375 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001376
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001377 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1378 ++fgap->ga_len;
1379 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001380 }
1381 }
1382
1383 // class members
1384 else if (has_static)
1385 {
1386 // class members (public, read access, private):
1387 // "static _varname"
1388 // "static varname"
1389 // "public static varname"
1390 char_u *varname = p;
1391 char_u *varname_end = NULL;
1392 type_T *type = NULL;
1393 char_u *init_expr = NULL;
1394 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001395 &varname_end, &type_list, &type,
1396 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001397 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001398 if (is_duplicate_member(&classmembers, varname, varname_end))
1399 {
1400 vim_free(init_expr);
1401 break;
1402 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001403 if (add_member(&classmembers, varname, varname_end,
1404 has_public, type, init_expr) == FAIL)
1405 {
1406 vim_free(init_expr);
1407 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001408 }
1409 }
1410
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001411 else
1412 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001413 if (is_class)
1414 semsg(_(e_not_valid_command_in_class_str), line);
1415 else
1416 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001417 break;
1418 }
1419 }
1420 vim_free(theline);
1421
Bram Moolenaar83677162023-01-08 19:54:10 +00001422 class_T *extends_cl = NULL; // class from "extends" argument
1423
1424 /*
1425 * Check a few things before defining the class.
1426 */
1427
1428 // Check the "extends" class is valid.
1429 if (success && extends != NULL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001430 success = validate_extends_class(extends, &extends_cl);
Bram Moolenaar83677162023-01-08 19:54:10 +00001431 VIM_CLEAR(extends);
1432
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001433 // Check the new class members and object members doesn't duplicate the
1434 // members in the extended class lineage.
1435 if (success && extends_cl != NULL)
1436 success = validate_extends_members(&classmembers, &objmembers,
1437 extends_cl);
1438
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001439 class_T **intf_classes = NULL;
1440
Bram Moolenaar83677162023-01-08 19:54:10 +00001441 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001442 if (success && ga_impl.ga_len > 0)
1443 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001444 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1445
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001446 success = validate_implements_classes(&ga_impl, intf_classes,
1447 &classfunctions, &classmembers,
1448 &objmethods, &objmembers);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001449 }
1450
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001451 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001452 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001453 success = check_func_arg_names(&classfunctions, &objmethods,
1454 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001455
Bram Moolenaareb533502022-12-14 15:06:11 +00001456 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001457 if (success)
1458 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001459 // "endclass" encountered without failures: Create the class.
1460
Bram Moolenaareb533502022-12-14 15:06:11 +00001461 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001462 if (cl == NULL)
1463 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001464 if (!is_class)
1465 cl->class_flags = CLASS_INTERFACE;
1466
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001467 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001468 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001469 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001470 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001471
Bram Moolenaard0200c82023-01-28 15:19:40 +00001472 if (extends_cl != NULL)
1473 {
1474 cl->class_extends = extends_cl;
1475 extends_cl->class_flags |= CLASS_EXTENDED;
1476 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001477
Bram Moolenaard505d172022-12-18 21:42:55 +00001478 // Add class and object members to "cl".
1479 if (add_members_to_class(&classmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001480 extends_cl == NULL ? NULL
1481 : extends_cl->class_class_members,
1482 extends_cl == NULL ? 0
1483 : extends_cl->class_class_member_count,
1484 &cl->class_class_members,
1485 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001486 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001487 extends_cl == NULL ? NULL
1488 : extends_cl->class_obj_members,
1489 extends_cl == NULL ? 0
1490 : extends_cl->class_obj_member_count,
1491 &cl->class_obj_members,
1492 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001493 goto cleanup;
1494
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001495 if (ga_impl.ga_len > 0)
1496 {
1497 // Move the "implements" names into the class.
1498 cl->class_interface_count = ga_impl.ga_len;
1499 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1500 if (cl->class_interfaces == NULL)
1501 goto cleanup;
1502 for (int i = 0; i < ga_impl.ga_len; ++i)
1503 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1504 VIM_CLEAR(ga_impl.ga_data);
1505 ga_impl.ga_len = 0;
1506
Bram Moolenaard0200c82023-01-28 15:19:40 +00001507 cl->class_interfaces_cl = intf_classes;
1508 intf_classes = NULL;
1509 }
1510
1511 if (cl->class_interface_count > 0 || extends_cl != NULL)
1512 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001513 // Add a method and member lookup table to each of the interface
1514 // classes.
1515 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1516 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001517 }
1518
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001519 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001520 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001521 add_class_members(cl, eap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001522
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001523 int have_new = FALSE;
1524 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001525 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001526 {
1527 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1528 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001529 {
1530 have_new = TRUE;
1531 break;
1532 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001533 }
1534
1535 if (have_new)
1536 // The return type of new() is an object of class "cl"
1537 class_func->uf_ret_type->tt_class = cl;
1538 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001539 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001540 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001541
Bram Moolenaar58b40092023-01-11 15:59:05 +00001542 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001543 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1544 &objmethods) == FAIL)
1545 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001546
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001547 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001548 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001549 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001550 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001551 cl->class_type_list = type_list;
1552
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001553 class_created(cl);
1554
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001555 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001556 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001557
1558 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001559 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02001560 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001561 typval_T tv;
1562 tv.v_type = VAR_CLASS;
1563 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001564 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001565 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001566 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001567 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001568 return;
1569 }
1570
1571cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00001572 if (cl != NULL)
1573 {
1574 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001575 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001576 if (cl->class_interfaces != NULL)
1577 {
1578 for (int i = 0; i < cl->class_interface_count; ++i)
1579 vim_free(cl->class_interfaces[i]);
1580 vim_free(cl->class_interfaces);
1581 }
1582 if (cl->class_interfaces_cl != NULL)
1583 {
1584 for (int i = 0; i < cl->class_interface_count; ++i)
1585 class_unref(cl->class_interfaces_cl[i]);
1586 vim_free(cl->class_interfaces_cl);
1587 }
Bram Moolenaareb533502022-12-14 15:06:11 +00001588 vim_free(cl->class_obj_members);
1589 vim_free(cl->class_obj_methods);
1590 vim_free(cl);
1591 }
1592
Bram Moolenaar83677162023-01-08 19:54:10 +00001593 vim_free(extends);
1594 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001595
1596 if (intf_classes != NULL)
1597 {
1598 for (int i = 0; i < ga_impl.ga_len; ++i)
1599 class_unref(intf_classes[i]);
1600 vim_free(intf_classes);
1601 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001602 ga_clear_strings(&ga_impl);
1603
Bram Moolenaard505d172022-12-18 21:42:55 +00001604 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001605 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001606 garray_T *gap = round == 1 ? &classmembers : &objmembers;
1607 if (gap->ga_len == 0 || gap->ga_data == NULL)
1608 continue;
1609
1610 for (int i = 0; i < gap->ga_len; ++i)
1611 {
1612 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
1613 vim_free(m->ocm_name);
1614 vim_free(m->ocm_init);
1615 }
1616 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001617 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001618
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001619 for (int i = 0; i < objmethods.ga_len; ++i)
1620 {
1621 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
1622 func_clear_free(uf, FALSE);
1623 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001624 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001625
1626 for (int i = 0; i < classfunctions.ga_len; ++i)
1627 {
1628 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
1629 func_clear_free(uf, FALSE);
1630 }
1631 ga_clear(&classfunctions);
1632
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001633 clear_type_list(&type_list);
1634}
1635
1636/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001637 * Find member "name" in class "cl", set "member_idx" to the member index and
1638 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001639 * When "is_object" is TRUE, then look for object members. Otherwise look for
1640 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001641 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02001642 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001643 */
1644 type_T *
1645class_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001646 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001647 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001648 char_u *name,
1649 char_u *name_end,
1650 int *member_idx,
Ernie Rael456ae552023-09-01 18:54:54 +02001651 ocmember_T **p_m)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001652{
1653 *member_idx = -1; // not found (yet)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001654 size_t len = name_end - name;
1655 int member_count = is_object ? cl->class_obj_member_count
1656 : cl->class_class_member_count;
1657 ocmember_T *members = is_object ? cl->class_obj_members
1658 : cl->class_class_members;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001659
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001660 for (int i = 0; i < member_count; ++i)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001661 {
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001662 ocmember_T *m = members + i;
Bram Moolenaard505d172022-12-18 21:42:55 +00001663 if (STRNCMP(m->ocm_name, name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001664 {
1665 *member_idx = i;
Ernie Rael456ae552023-09-01 18:54:54 +02001666 if (p_m != NULL)
1667 *p_m = m;
Bram Moolenaard505d172022-12-18 21:42:55 +00001668 return m->ocm_type;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001669 }
1670 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001671
1672 semsg(_(e_unknown_variable_str), name);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001673 return &t_any;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001674}
1675
1676/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001677 * Handle ":enum" up to ":endenum".
1678 */
1679 void
1680ex_enum(exarg_T *eap UNUSED)
1681{
1682 // TODO
1683}
1684
1685/*
1686 * Handle ":type".
1687 */
1688 void
1689ex_type(exarg_T *eap UNUSED)
1690{
1691 // TODO
1692}
1693
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001694/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001695 * Returns OK if a member variable named "name" is present in the class "cl".
1696 * Otherwise returns FAIL. If found, the member variable typval is set in
1697 * "rettv". If "is_object" is TRUE, then the object member variable table is
1698 * searched. Otherwise the class member variable table is searched.
1699 */
1700 static int
1701get_member_tv(
1702 class_T *cl,
1703 int is_object,
1704 char_u *name,
1705 size_t namelen,
1706 typval_T *rettv)
1707{
1708 int member_count = is_object ? cl->class_obj_member_count
1709 : cl->class_class_member_count;
1710 ocmember_T *members = is_object ? cl->class_obj_members
1711 : cl->class_class_members;
1712
1713 for (int i = 0; i < member_count; ++i)
1714 {
1715 ocmember_T *m = &members[i];
1716 if (STRNCMP(name, m->ocm_name, namelen) == 0
1717 && m->ocm_name[namelen] == NUL)
1718 {
1719 if (*name == '_')
1720 {
1721 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
1722 return FAIL;
1723 }
1724
1725 // The object only contains a pointer to the class, the member
1726 // values array follows right after that.
1727 object_T *obj = rettv->vval.v_object;
1728 if (is_object)
1729 {
1730 typval_T *tv = (typval_T *)(obj + 1) + i;
1731 copy_tv(tv, rettv);
1732 }
1733 else
1734 copy_tv(&cl->class_members_tv[i], rettv);
1735
1736 object_unref(obj);
1737
1738 return OK;
1739 }
1740 }
1741
1742 return FAIL;
1743}
1744
1745/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001746 * Evaluate what comes after a class:
1747 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001748 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001749 * - class constructor: SomeClass.new()
1750 * - object member: someObject.varname
1751 * - object method: someObject.SomeMethod()
1752 *
1753 * "*arg" points to the '.'.
1754 * "*arg" is advanced to after the member name or method call.
1755 *
1756 * Returns FAIL or OK.
1757 */
1758 int
1759class_object_index(
1760 char_u **arg,
1761 typval_T *rettv,
1762 evalarg_T *evalarg,
1763 int verbose UNUSED) // give error messages
1764{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001765 if (VIM_ISWHITE((*arg)[1]))
1766 {
1767 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
1768 return FAIL;
1769 }
1770
1771 ++*arg;
1772 char_u *name = *arg;
1773 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
1774 if (name_end == name)
1775 return FAIL;
1776 size_t len = name_end - name;
1777
Bram Moolenaar552bdca2023-02-17 21:08:50 +00001778 class_T *cl;
1779 if (rettv->v_type == VAR_CLASS)
1780 cl = rettv->vval.v_class;
1781 else // VAR_OBJECT
1782 {
1783 if (rettv->vval.v_object == NULL)
1784 {
1785 emsg(_(e_using_null_object));
1786 return FAIL;
1787 }
1788 cl = rettv->vval.v_object->obj_class;
1789 }
1790
Bram Moolenaard13dd302023-03-11 20:56:35 +00001791 if (cl == NULL)
1792 {
1793 emsg(_(e_incomplete_type));
1794 return FAIL;
1795 }
1796
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001797 if (*name_end == '(')
1798 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001799 int on_class = rettv->v_type == VAR_CLASS;
1800 int count = on_class ? cl->class_class_function_count
1801 : cl->class_obj_method_count;
1802 for (int i = 0; i < count; ++i)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001803 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001804 ufunc_T *fp = on_class ? cl->class_class_functions[i]
1805 : cl->class_obj_methods[i];
Bram Moolenaar4ae00572022-12-09 22:49:23 +00001806 // Use a separate pointer to avoid that ASAN complains about
1807 // uf_name[] only being 4 characters.
1808 char_u *ufname = (char_u *)fp->uf_name;
1809 if (STRNCMP(name, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001810 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001811 typval_T argvars[MAX_FUNC_ARGS + 1];
1812 int argcount = 0;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001813
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001814 if (*ufname == '_')
Yegappan Lakshmanancd7293b2023-08-27 19:18:23 +02001815 {
1816 // Cannot access a private method outside of a class
1817 semsg(_(e_cannot_access_private_method_str), name);
1818 return FAIL;
1819 }
1820
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001821 char_u *argp = name_end;
1822 int ret = get_func_arguments(&argp, evalarg, 0,
1823 argvars, &argcount);
1824 if (ret == FAIL)
1825 return FAIL;
1826
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001827 funcexe_T funcexe;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001828 CLEAR_FIELD(funcexe);
1829 funcexe.fe_evaluate = TRUE;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001830 if (rettv->v_type == VAR_OBJECT)
1831 {
1832 funcexe.fe_object = rettv->vval.v_object;
1833 ++funcexe.fe_object->obj_refcount;
1834 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001835
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001836 // Clear the class or object after calling the function, in
1837 // case the refcount is one.
1838 typval_T tv_tofree = *rettv;
1839 rettv->v_type = VAR_UNKNOWN;
1840
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001841 // Call the user function. Result goes into rettv;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001842 int error = call_user_func_check(fp, argcount, argvars,
1843 rettv, &funcexe, NULL);
1844
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001845 // Clear the previous rettv and the arguments.
1846 clear_tv(&tv_tofree);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001847 for (int idx = 0; idx < argcount; ++idx)
1848 clear_tv(&argvars[idx]);
1849
1850 if (error != FCERR_NONE)
1851 {
1852 user_func_error(error, printable_func_name(fp),
1853 funcexe.fe_found_var);
1854 return FAIL;
1855 }
1856 *arg = argp;
1857 return OK;
1858 }
1859 }
1860
1861 semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name);
1862 }
1863
1864 else if (rettv->v_type == VAR_OBJECT)
1865 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001866 // Search in the object member variable table and the class member
1867 // variable table.
1868 if (get_member_tv(cl, TRUE, name, len, rettv) == OK
1869 || get_member_tv(cl, FALSE, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001870 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001871 *arg = name_end;
1872 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001873 }
1874
1875 semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
1876 }
1877
Bram Moolenaard505d172022-12-18 21:42:55 +00001878 else if (rettv->v_type == VAR_CLASS)
1879 {
1880 // class member
1881 for (int i = 0; i < cl->class_class_member_count; ++i)
1882 {
1883 ocmember_T *m = &cl->class_class_members[i];
1884 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
1885 {
1886 if (*name == '_')
1887 {
1888 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
1889 return FAIL;
1890 }
Ernie Rael18143d32023-09-04 22:30:41 +02001891 if ((cl->class_flags & CLASS_INTERFACE) != 0)
1892 {
1893 semsg(_(e_interface_static_direct_access_str),
1894 cl->class_name, m->ocm_name);
1895 return FAIL;
1896 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001897
1898 typval_T *tv = &cl->class_members_tv[i];
1899 copy_tv(tv, rettv);
1900 class_unref(cl);
1901
1902 *arg = name_end;
1903 return OK;
1904 }
1905 }
1906
1907 semsg(_(e_member_not_found_on_class_str_str), cl->class_name, name);
1908 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001909
1910 return FAIL;
1911}
1912
1913/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001914 * If "arg" points to a class or object method, return it.
1915 * Otherwise return NULL.
1916 */
1917 ufunc_T *
1918find_class_func(char_u **arg)
1919{
1920 char_u *name = *arg;
1921 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
1922 if (name_end == name || *name_end != '.')
1923 return NULL;
1924
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001925 size_t len = name_end - name;
1926 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001927 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00001928 if (eval_variable(name, (int)len,
1929 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001930 return NULL;
1931 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00001932 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001933
1934 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
1935 : tv.vval.v_object->obj_class;
1936 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00001937 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001938 char_u *fname = name_end + 1;
1939 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
1940 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00001941 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001942 len = fname_end - fname;
1943
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001944 int count = tv.v_type == VAR_CLASS ? cl->class_class_function_count
1945 : cl->class_obj_method_count;
1946 ufunc_T **funcs = tv.v_type == VAR_CLASS ? cl->class_class_functions
1947 : cl->class_obj_methods;
1948 for (int i = 0; i < count; ++i)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001949 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001950 ufunc_T *fp = funcs[i];
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001951 // Use a separate pointer to avoid that ASAN complains about
1952 // uf_name[] only being 4 characters.
1953 char_u *ufname = (char_u *)fp->uf_name;
1954 if (STRNCMP(fname, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaareb533502022-12-14 15:06:11 +00001955 {
1956 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001957 return fp;
Bram Moolenaareb533502022-12-14 15:06:11 +00001958 }
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001959 }
1960
Bram Moolenaareb533502022-12-14 15:06:11 +00001961fail_after_eval:
1962 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001963 return NULL;
1964}
1965
1966/*
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001967 * If "name[len]" is a class member in cctx->ctx_ufunc->uf_class return the
1968 * index in class.class_class_members[].
1969 * If "cl_ret" is not NULL set it to the class.
1970 * Otherwise return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001971 */
1972 int
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001973class_member_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001974{
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001975 if (cctx == NULL || cctx->ctx_ufunc == NULL
1976 || cctx->ctx_ufunc->uf_class == NULL)
1977 return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001978 class_T *cl = cctx->ctx_ufunc->uf_class;
1979
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001980 for (int i = 0; i < cl->class_class_member_count; ++i)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001981 {
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001982 ocmember_T *m = &cl->class_class_members[i];
1983 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001984 {
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001985 if (cl_ret != NULL)
1986 *cl_ret = cl;
1987 return i;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001988 }
1989 }
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001990 return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001991}
1992
1993/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00001994 * Return TRUE if current context "cctx_arg" is inside class "cl".
1995 * Return FALSE if not.
1996 */
1997 int
1998inside_class(cctx_T *cctx_arg, class_T *cl)
1999{
2000 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002001 if (cctx->ctx_ufunc != NULL
2002 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002003 return TRUE;
2004 return FALSE;
2005}
2006
2007/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002008 * Make a copy of an object.
2009 */
2010 void
2011copy_object(typval_T *from, typval_T *to)
2012{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002013 if (from->vval.v_object == NULL)
2014 to->vval.v_object = NULL;
2015 else
2016 {
2017 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002018 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002019 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002020}
2021
2022/*
2023 * Free an object.
2024 */
2025 static void
2026object_clear(object_T *obj)
2027{
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002028 // Avoid a recursive call, it can happen if "obj" has a circular reference.
2029 obj->obj_refcount = INT_MAX;
2030
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002031 class_T *cl = obj->obj_class;
2032
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002033 if (!cl)
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002034 return;
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002035
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002036 // the member values are just after the object structure
2037 typval_T *tv = (typval_T *)(obj + 1);
2038 for (int i = 0; i < cl->class_obj_member_count; ++i)
2039 clear_tv(tv + i);
2040
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002041 // Remove from the list headed by "first_object".
2042 object_cleared(obj);
2043
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002044 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002045 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002046}
2047
2048/*
2049 * Unreference an object.
2050 */
2051 void
2052object_unref(object_T *obj)
2053{
2054 if (obj != NULL && --obj->obj_refcount <= 0)
2055 object_clear(obj);
2056}
2057
2058/*
2059 * Make a copy of a class.
2060 */
2061 void
2062copy_class(typval_T *from, typval_T *to)
2063{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002064 if (from->vval.v_class == NULL)
2065 to->vval.v_class = NULL;
2066 else
2067 {
2068 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002069 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002070 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002071}
2072
2073/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002074 * Free the class "cl" and its contents.
2075 */
2076 static void
2077class_free(class_T *cl)
2078{
2079 // Freeing what the class contains may recursively come back here.
2080 // Clear "class_name" first, if it is NULL the class does not need to
2081 // be freed.
2082 VIM_CLEAR(cl->class_name);
2083
2084 class_unref(cl->class_extends);
2085
2086 for (int i = 0; i < cl->class_interface_count; ++i)
2087 {
2088 vim_free(((char_u **)cl->class_interfaces)[i]);
2089 if (cl->class_interfaces_cl[i] != NULL)
2090 class_unref(cl->class_interfaces_cl[i]);
2091 }
2092 vim_free(cl->class_interfaces);
2093 vim_free(cl->class_interfaces_cl);
2094
2095 itf2class_T *next;
2096 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2097 {
2098 next = i2c->i2c_next;
2099 vim_free(i2c);
2100 }
2101
2102 for (int i = 0; i < cl->class_class_member_count; ++i)
2103 {
2104 ocmember_T *m = &cl->class_class_members[i];
2105 vim_free(m->ocm_name);
2106 vim_free(m->ocm_init);
2107 if (cl->class_members_tv != NULL)
2108 clear_tv(&cl->class_members_tv[i]);
2109 }
2110 vim_free(cl->class_class_members);
2111 vim_free(cl->class_members_tv);
2112
2113 for (int i = 0; i < cl->class_obj_member_count; ++i)
2114 {
2115 ocmember_T *m = &cl->class_obj_members[i];
2116 vim_free(m->ocm_name);
2117 vim_free(m->ocm_init);
2118 }
2119 vim_free(cl->class_obj_members);
2120
2121 for (int i = 0; i < cl->class_class_function_count; ++i)
2122 {
2123 ufunc_T *uf = cl->class_class_functions[i];
2124 func_clear_free(uf, FALSE);
2125 }
2126 vim_free(cl->class_class_functions);
2127
2128 for (int i = 0; i < cl->class_obj_method_count; ++i)
2129 {
2130 ufunc_T *uf = cl->class_obj_methods[i];
2131 func_clear_free(uf, FALSE);
2132 }
2133 vim_free(cl->class_obj_methods);
2134
2135 clear_type_list(&cl->class_type_list);
2136
2137 class_cleared(cl);
2138
2139 vim_free(cl);
2140}
2141
2142/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002143 * Unreference a class. Free it when the reference count goes down to zero.
2144 */
2145 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002146class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002147{
Bram Moolenaard505d172022-12-18 21:42:55 +00002148 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002149 class_free(cl);
2150}
2151
2152/*
2153 * Go through the list of all classes and free items without "copyID".
2154 */
2155 int
2156class_free_nonref(int copyID)
2157{
2158 int did_free = FALSE;
2159
2160 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002161 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002162 next_nonref_class = cl->class_next_used;
2163 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002164 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002165 // Free the class and items it contains.
2166 class_free(cl);
2167 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002168 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002169 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002170
2171 next_nonref_class = NULL;
2172 return did_free;
2173}
2174
2175 int
2176set_ref_in_classes(int copyID)
2177{
2178 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
2179 set_ref_in_item_class(cl, copyID, NULL, NULL);
2180
2181 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002182}
2183
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002184static object_T *first_object = NULL;
2185
2186/*
2187 * Call this function when an object has been created. It will be added to the
2188 * list headed by "first_object".
2189 */
2190 void
2191object_created(object_T *obj)
2192{
2193 if (first_object != NULL)
2194 {
2195 obj->obj_next_used = first_object;
2196 first_object->obj_prev_used = obj;
2197 }
2198 first_object = obj;
2199}
2200
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002201static object_T *next_nonref_obj = NULL;
2202
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002203/*
2204 * Call this function when an object has been cleared and is about to be freed.
2205 * It is removed from the list headed by "first_object".
2206 */
2207 void
2208object_cleared(object_T *obj)
2209{
2210 if (obj->obj_next_used != NULL)
2211 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
2212 if (obj->obj_prev_used != NULL)
2213 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
2214 else if (first_object == obj)
2215 first_object = obj->obj_next_used;
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002216
2217 // update the next object to check if needed
2218 if (obj == next_nonref_obj)
2219 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002220}
2221
2222/*
2223 * Go through the list of all objects and free items without "copyID".
2224 */
2225 int
2226object_free_nonref(int copyID)
2227{
2228 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002229
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002230 for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002231 {
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002232 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002233 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
2234 {
2235 // Free the object and items it contains.
2236 object_clear(obj);
2237 did_free = TRUE;
2238 }
2239 }
2240
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002241 next_nonref_obj = NULL;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002242 return did_free;
2243}
2244
LemonBoyafe04662023-08-23 21:08:11 +02002245/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002246 * Return TRUE when the class "cl", its base class or one of the implemented
2247 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02002248 */
2249 int
2250class_instance_of(class_T *cl, class_T *other_cl)
2251{
2252 if (cl == other_cl)
2253 return TRUE;
2254
2255 // Recursively check the base classes.
2256 for (; cl != NULL; cl = cl->class_extends)
2257 {
2258 if (cl == other_cl)
2259 return TRUE;
2260 // Check the implemented interfaces.
2261 for (int i = cl->class_interface_count - 1; i >= 0; --i)
2262 if (cl->class_interfaces_cl[i] == other_cl)
2263 return TRUE;
2264 }
2265
2266 return FALSE;
2267}
2268
2269/*
2270 * "instanceof(object, classinfo)" function
2271 */
2272 void
2273f_instanceof(typval_T *argvars, typval_T *rettv)
2274{
2275 typval_T *object_tv = &argvars[0];
2276 typval_T *classinfo_tv = &argvars[1];
2277 listitem_T *li;
2278
2279 rettv->vval.v_number = VVAL_FALSE;
2280
2281 if (check_for_object_arg(argvars, 0) == FAIL
2282 || check_for_class_or_list_arg(argvars, 1) == FAIL)
2283 return;
2284
2285 if (classinfo_tv->v_type == VAR_LIST)
2286 {
2287 FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
2288 {
2289 if (li->li_tv.v_type != VAR_CLASS)
2290 {
2291 emsg(_(e_class_required));
2292 return;
2293 }
2294
2295 if (class_instance_of(object_tv->vval.v_object->obj_class,
2296 li->li_tv.vval.v_class) == TRUE)
2297 {
2298 rettv->vval.v_number = VVAL_TRUE;
2299 return;
2300 }
2301 }
2302 }
2303 else if (classinfo_tv->v_type == VAR_CLASS)
2304 {
2305 rettv->vval.v_number = class_instance_of(object_tv->vval.v_object->obj_class,
2306 classinfo_tv->vval.v_class);
2307 }
2308}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002309
2310#endif // FEAT_EVAL