blob: ccc38183854456429ceb0c385ed66f6300fa6ee1 [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;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +0200272 int m_idx = class_member_idx(i2c->i2c_class, name, 0);
273 if (m_idx >= 0)
274 return m_idx;
275
Ernie Rael18143d32023-09-04 22:30:41 +0200276 siemsg("class %s, interface %s, static %s not found",
277 cl->class_name, itf->class_name, name);
278 return 0;
279 }
280 else
281 {
282 // A table follows the i2c for the class
283 int *table = (int *)(i2c + 1);
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200284 // "method_offset" is 0, if method is in the current class. If method
285 // is in a parent class, then it is non-zero.
286 return table[idx] + method_offset;
Ernie Rael18143d32023-09-04 22:30:41 +0200287 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000288}
289
290/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200291 * Check whether a class named "extends_name" is present. If the class is
292 * valid, then "extends_clp" is set with the class pointer.
293 * Returns TRUE if the class name "extends_names" is a valid class.
294 */
295 static int
296validate_extends_class(char_u *extends_name, class_T **extends_clp)
297{
298 typval_T tv;
299 int success = FALSE;
300
301 tv.v_type = VAR_UNKNOWN;
302 if (eval_variable_import(extends_name, &tv) == FAIL)
303 {
304 semsg(_(e_class_name_not_found_str), extends_name);
305 return success;
306 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200307
308 if (tv.v_type != VAR_CLASS
309 || tv.vval.v_class == NULL
310 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
311 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200312 else
313 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200314 class_T *extends_cl = tv.vval.v_class;
315 ++extends_cl->class_refcount;
316 *extends_clp = extends_cl;
317 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200318 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200319 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200320
321 return success;
322}
323
324/*
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200325 * Check whether a class/object member variable in "classmembers_gap" /
326 * "objmembers_gap" is a duplicate of a member in any of the extended parent
327 * class lineage. Returns TRUE if there are no duplicates.
328 */
329 static int
330validate_extends_members(
331 garray_T *classmembers_gap,
332 garray_T *objmembers_gap,
333 class_T *extends_cl)
334{
335 for (int loop = 1; loop <= 2; ++loop)
336 {
337 // loop == 1: check class members
338 // loop == 2: check object members
339 int member_count = loop == 1 ? classmembers_gap->ga_len
340 : objmembers_gap->ga_len;
341 if (member_count == 0)
342 continue;
343 ocmember_T *members = (ocmember_T *)(loop == 1
344 ? classmembers_gap->ga_data
345 : objmembers_gap->ga_data);
346
347 // Validate each member variable
348 for (int c_i = 0; c_i < member_count; c_i++)
349 {
350 class_T *p_cl = extends_cl;
351 ocmember_T *c_m = members + c_i;
352 char_u *pstr = (*c_m->ocm_name == '_')
353 ? c_m->ocm_name + 1 : c_m->ocm_name;
354
355 // Check in all the parent classes in the lineage
356 while (p_cl != NULL)
357 {
358 int p_member_count = loop == 1
359 ? p_cl->class_class_member_count
360 : p_cl->class_obj_member_count;
361 if (p_member_count == 0)
362 continue;
363 ocmember_T *p_members = (loop == 1
364 ? p_cl->class_class_members
365 : p_cl->class_obj_members);
366
367 // Compare against all the members in the parent class
368 for (int p_i = 0; p_i < p_member_count; p_i++)
369 {
370 ocmember_T *p_m = p_members + p_i;
371 char_u *qstr = (*p_m->ocm_name == '_')
372 ? p_m->ocm_name + 1 : p_m->ocm_name;
373 if (STRCMP(pstr, qstr) == 0)
374 {
375 semsg(_(e_duplicate_member_str), c_m->ocm_name);
376 return FALSE;
377 }
378 }
379
380 p_cl = p_cl->class_extends;
381 }
382 }
383 }
384
385 return TRUE;
386}
387
388/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200389 * When extending an abstract class, check whether all the abstract methods in
390 * the parent class are implemented. Returns TRUE if all the methods are
391 * implemented.
392 */
393 static int
394validate_extends_methods(
395 garray_T *classmethods_gap,
396 garray_T *objmethods_gap,
397 class_T *extends_cl)
398{
399 for (int loop = 1; loop <= 2; ++loop)
400 {
401 // loop == 1: check class methods
402 // loop == 2: check object methods
403 int extends_method_count = loop == 1
404 ? extends_cl->class_class_function_count
405 : extends_cl->class_obj_method_count;
406 if (extends_method_count == 0)
407 continue;
408
409 ufunc_T **extends_methods = loop == 1
410 ? extends_cl->class_class_functions
411 : extends_cl->class_obj_methods;
412
413 int method_count = loop == 1 ? classmethods_gap->ga_len
414 : objmethods_gap->ga_len;
415 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
416 ? classmethods_gap->ga_data
417 : objmethods_gap->ga_data);
418
419 for (int i = 0; i < extends_method_count; i++)
420 {
421 ufunc_T *uf = extends_methods[i];
422 if ((uf->uf_flags & FC_ABSTRACT) == 0)
423 continue;
424
425 int method_found = FALSE;
426
427 for (int j = 0; j < method_count; j++)
428 {
429 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
430 {
431 method_found = TRUE;
432 break;
433 }
434 }
435
436 if (!method_found)
437 {
438 semsg(_(e_abstract_method_str_not_found), uf->uf_name);
439 return FALSE;
440 }
441 }
442 }
443
444 return TRUE;
445}
446
447/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200448 * Check the members of the interface class "ifcl" match the class members
449 * ("classmembers_gap") and object members ("objmembers_gap") of a class.
450 * Returns TRUE if the class and object member names are valid.
451 */
452 static int
453validate_interface_members(
454 char_u *intf_class_name,
455 class_T *ifcl,
456 garray_T *classmembers_gap,
457 garray_T *objmembers_gap)
458{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200459 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200460 {
461 // loop == 1: check class members
462 // loop == 2: check object members
463 int if_count = loop == 1 ? ifcl->class_class_member_count
464 : ifcl->class_obj_member_count;
465 if (if_count == 0)
466 continue;
467 ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
468 : ifcl->class_obj_members;
469 ocmember_T *cl_ms = (ocmember_T *)(loop == 1
470 ? classmembers_gap->ga_data
471 : objmembers_gap->ga_data);
472 int cl_count = loop == 1 ? classmembers_gap->ga_len
473 : objmembers_gap->ga_len;
474 for (int if_i = 0; if_i < if_count; ++if_i)
475 {
476 int cl_i;
477 for (cl_i = 0; cl_i < cl_count; ++cl_i)
478 {
479 ocmember_T *m = &cl_ms[cl_i];
480 where_T where = WHERE_INIT;
481
482 if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) != 0)
483 continue;
484
485 // Ensure the type is matching.
486 where.wt_func_name = (char *)m->ocm_name;
487 where.wt_kind = WT_MEMBER;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200488 if (check_type(if_ms[if_i].ocm_type, m->ocm_type, TRUE,
489 where) == FAIL)
490 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200491
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200492 if (if_ms[if_i].ocm_access != m->ocm_access)
493 {
494 semsg(_(e_member_str_of_interface_str_has_different_access),
495 if_ms[if_i].ocm_name, intf_class_name);
496 return FALSE;
497 }
498
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200499 break;
500 }
501 if (cl_i == cl_count)
502 {
503 semsg(_(e_member_str_of_interface_str_not_implemented),
504 if_ms[if_i].ocm_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200505 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200506 }
507 }
508 }
509
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200510 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200511}
512
513/*
514 * Check the functions/methods of the interface class "ifcl" match the class
515 * methods ("classfunctions_gap") and object functions ("objmemthods_gap") of a
516 * class.
517 * Returns TRUE if the class and object member names are valid.
518 */
519 static int
520validate_interface_methods(
521 char_u *intf_class_name,
522 class_T *ifcl,
523 garray_T *classfunctions_gap,
524 garray_T *objmethods_gap)
525{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200526 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200527 {
528 // loop == 1: check class functions
529 // loop == 2: check object methods
530 int if_count = loop == 1 ? ifcl->class_class_function_count
531 : ifcl->class_obj_method_count;
532 if (if_count == 0)
533 continue;
534 ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
535 : ifcl->class_obj_methods;
536 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
537 ? classfunctions_gap->ga_data
538 : objmethods_gap->ga_data);
539 int cl_count = loop == 1 ? classfunctions_gap->ga_len
540 : objmethods_gap->ga_len;
541 for (int if_i = 0; if_i < if_count; ++if_i)
542 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200543 char_u *if_name = if_fp[if_i]->uf_name;
544 int cl_i;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200545 for (cl_i = 0; cl_i < cl_count; ++cl_i)
546 {
547 char_u *cl_name = cl_fp[cl_i]->uf_name;
548 if (STRCMP(if_name, cl_name) == 0)
549 {
550 where_T where = WHERE_INIT;
551
552 // Ensure the type is matching.
553 where.wt_func_name = (char *)if_name;
554 where.wt_kind = WT_METHOD;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200555 if (check_type(if_fp[if_i]->uf_func_type,
556 cl_fp[cl_i]->uf_func_type, TRUE, where) == FAIL)
557 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200558 break;
559 }
560 }
561 if (cl_i == cl_count)
562 {
563 semsg(_(e_function_str_of_interface_str_not_implemented),
564 if_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200565 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200566 }
567 }
568 }
569
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200570 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200571}
572
573/*
574 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200575 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200576 * object methods and object members in the new class are in
577 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
578 * "objmembers_gap" respectively.
579 */
580 static int
581validate_implements_classes(
582 garray_T *impl_gap,
583 class_T **intf_classes,
584 garray_T *classfunctions_gap,
585 garray_T *classmembers_gap,
586 garray_T *objmethods_gap,
587 garray_T *objmembers_gap)
588{
589 int success = TRUE;
590
591 for (int i = 0; i < impl_gap->ga_len && success; ++i)
592 {
593 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
594 typval_T tv;
595 tv.v_type = VAR_UNKNOWN;
596 if (eval_variable_import(impl, &tv) == FAIL)
597 {
598 semsg(_(e_interface_name_not_found_str), impl);
599 success = FALSE;
600 break;
601 }
602
603 if (tv.v_type != VAR_CLASS
604 || tv.vval.v_class == NULL
605 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
606 {
607 semsg(_(e_not_valid_interface_str), impl);
608 success = FALSE;
609 clear_tv(&tv);
610 break;
611 }
612
613 class_T *ifcl = tv.vval.v_class;
614 intf_classes[i] = ifcl;
615 ++ifcl->class_refcount;
616
617 // check the members of the interface match the members of the class
618 success = validate_interface_members(impl, ifcl, classmembers_gap,
619 objmembers_gap);
620
621 // check the functions/methods of the interface match the
622 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200623 if (success)
624 success = validate_interface_methods(impl, ifcl,
625 classfunctions_gap, objmethods_gap);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200626 clear_tv(&tv);
627 }
628
629 return success;
630}
631
632/*
633 * Check no function argument name is used as a class member.
634 * (Object members are always accessed with "this." prefix, so no need
635 * to check them.)
636 */
637 static int
638check_func_arg_names(
639 garray_T *classfunctions_gap,
640 garray_T *objmethods_gap,
641 garray_T *classmembers_gap)
642{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200643 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200644 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200645 {
646 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
647
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200648 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200649 {
650 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
651
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200652 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200653 {
654 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
655 garray_T *mgap = classmembers_gap;
656
657 // Check all the class member names
658 for (int mi = 0; mi < mgap->ga_len; ++mi)
659 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200660 char_u *mname =
661 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200662 if (STRCMP(aname, mname) == 0)
663 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200664 if (uf->uf_script_ctx.sc_sid > 0)
665 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
666
667 semsg(_(e_argument_already_declared_in_class_str),
668 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200669
670 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200671 }
672 }
673 }
674 }
675 }
676
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200677 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200678}
679
680/*
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200681 * Returns TRUE if the member "varname" is already defined.
682 */
683 static int
684is_duplicate_member(garray_T *mgap, char_u *varname, char_u *varname_end)
685{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200686 char_u *name = vim_strnsave(varname, varname_end - varname);
687 char_u *pstr = (*name == '_') ? name + 1 : name;
688 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200689
690 for (int i = 0; i < mgap->ga_len; ++i)
691 {
692 ocmember_T *m = ((ocmember_T *)mgap->ga_data) + i;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200693 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1 : m->ocm_name;
694 if (STRCMP(pstr, qstr) == 0)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200695 {
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200696 semsg(_(e_duplicate_member_str), name);
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200697 dup = TRUE;
698 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200699 }
700 }
701
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200702 vim_free(name);
703 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200704}
705
706/*
707 * Returns TRUE if the method "name" is already defined.
708 */
709 static int
710is_duplicate_method(garray_T *fgap, char_u *name)
711{
712 char_u *pstr = (*name == '_') ? name + 1 : name;
713
714 for (int i = 0; i < fgap->ga_len; ++i)
715 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200716 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
717 char_u *qstr = *n == '_' ? n + 1 : n;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200718 if (STRCMP(pstr, qstr) == 0)
719 {
720 semsg(_(e_duplicate_function_str), name);
721 return TRUE;
722 }
723 }
724
725 return FALSE;
726}
727
728/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200729 * Returns TRUE if the constructor is valid.
730 */
731 static int
732is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
733{
734 // Constructors are not allowed in abstract classes.
735 if (is_abstract)
736 {
737 emsg(_(e_cannot_define_new_function_in_abstract_class));
738 return FALSE;
739 }
740 // A constructor is always static, no need to define it so.
741 if (has_static)
742 {
743 emsg(_(e_cannot_define_new_function_as_static));
744 return FALSE;
745 }
746 // A return type should not be specified for the new()
747 // constructor method.
748 if (uf->uf_ret_type->tt_type != VAR_VOID)
749 {
750 emsg(_(e_cannot_use_a_return_type_with_new));
751 return FALSE;
752 }
753 return TRUE;
754}
755
756/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200757 * Update the interface class lookup table for the member index on the
758 * interface to the member index in the class implementing the interface.
759 * And a lookup table for the object method index on the interface
760 * to the object method index in the class implementing the interface.
761 * This is also used for updating the lookup table for the extended class
762 * hierarchy.
763 */
764 static int
765update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200766 class_T *ifcl,
767 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200768 garray_T *objmethods,
769 int pobj_method_offset,
770 int is_interface)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200771{
772 if (ifcl == NULL)
773 return OK;
774
775 // Table for members.
776 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200777 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200778 if (if2cl == NULL)
779 return FAIL;
780 if2cl->i2c_next = ifcl->class_itf2class;
781 ifcl->class_itf2class = if2cl;
782 if2cl->i2c_class = cl;
783 if2cl->i2c_is_method = FALSE;
784
785 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
786 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
787 {
788 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200789 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200790 {
791 int *table = (int *)(if2cl + 1);
792 table[if_i] = cl_i;
793 break;
794 }
795 }
796
797 // Table for methods.
798 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200799 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200800 if (if2cl == NULL)
801 return FAIL;
802 if2cl->i2c_next = ifcl->class_itf2class;
803 ifcl->class_itf2class = if2cl;
804 if2cl->i2c_class = cl;
805 if2cl->i2c_is_method = TRUE;
806
807 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
808 {
809 int done = FALSE;
810 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
811 {
812 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200813 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200814 {
815 int *table = (int *)(if2cl + 1);
816 table[if_i] = cl_i;
817 done = TRUE;
818 break;
819 }
820 }
821
822 // extended class object method is not overridden by the child class.
823 // Keep the method declared in one of the parent classes in the
824 // lineage.
825 if (!done && !is_interface)
826 {
827 // If "ifcl" is not the immediate parent of "cl", then search in
828 // the intermediate parent classes.
829 if (cl->class_extends != ifcl)
830 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200831 class_T *parent = cl->class_extends;
832 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200833
834 while (!done && parent != NULL && parent != ifcl)
835 {
836
837 for (int cl_i = 0;
838 cl_i < parent->class_obj_method_count_child; ++cl_i)
839 {
840 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
841 parent->class_obj_methods[cl_i]->uf_name)
842 == 0)
843 {
844 int *table = (int *)(if2cl + 1);
845 table[if_i] = method_offset + cl_i;
846 done = TRUE;
847 break;
848 }
849 }
850 method_offset += parent->class_obj_method_count_child;
851 parent = parent->class_extends;
852 }
853 }
854
855 if (!done)
856 {
857 int *table = (int *)(if2cl + 1);
858 table[if_i] = pobj_method_offset + if_i;
859 }
860 }
861 }
862
863 return OK;
864}
865
866/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200867 * Update the member and object method lookup tables for a new class in the
868 * interface class.
869 * For each interface add a lookup table for the member index on the interface
870 * to the member index in the new class. And a lookup table for the object
871 * method index on the interface to the object method index in the new class.
872 */
873 static int
874add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
875{
876 for (int i = 0; i < cl->class_interface_count; ++i)
877 {
878 class_T *ifcl = cl->class_interfaces_cl[i];
879
880 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
881 0, TRUE) == FAIL)
882 return FAIL;
883 }
884
885 // Update the lookup table for the extended class, if nay
886 if (extends_cl != NULL)
887 {
888 class_T *pclass = extends_cl;
889 int pobj_method_offset = objmethods_gap->ga_len;
890
891 // Update the entire lineage of extended classes.
892 while (pclass != NULL)
893 {
894 if (update_member_method_lookup_table(pclass, cl,
895 objmethods_gap, pobj_method_offset, FALSE) == FAIL)
896 return FAIL;
897
898 pobj_method_offset += pclass->class_obj_method_count_child;
899 pclass = pclass->class_extends;
900 }
901 }
902
903 return OK;
904}
905
906/*
907 * Add class members to a new class. Allocate a typval for each class member
908 * and initialize it.
909 */
910 static void
911add_class_members(class_T *cl, exarg_T *eap)
912{
913 // Allocate a typval for each class member and initialize it.
914 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
915 cl->class_class_member_count);
916 if (cl->class_members_tv == NULL)
917 return;
918
919 for (int i = 0; i < cl->class_class_member_count; ++i)
920 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200921 ocmember_T *m = &cl->class_class_members[i];
922 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200923 if (m->ocm_init != NULL)
924 {
925 typval_T *etv = eval_expr(m->ocm_init, eap);
926 if (etv != NULL)
927 {
928 *tv = *etv;
929 vim_free(etv);
930 }
931 }
932 else
933 {
934 // TODO: proper default value
935 tv->v_type = m->ocm_type->tt_type;
936 tv->vval.v_string = NULL;
937 }
938 }
939}
940
941/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +0200942 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200943 */
944 static void
945add_default_constructor(
946 class_T *cl,
947 garray_T *classfunctions_gap,
948 garray_T *type_list_gap)
949{
950 garray_T fga;
951
952 ga_init2(&fga, 1, 1000);
953 ga_concat(&fga, (char_u *)"new(");
954 for (int i = 0; i < cl->class_obj_member_count; ++i)
955 {
956 if (i > 0)
957 ga_concat(&fga, (char_u *)", ");
958 ga_concat(&fga, (char_u *)"this.");
959 ocmember_T *m = cl->class_obj_members + i;
960 ga_concat(&fga, (char_u *)m->ocm_name);
961 ga_concat(&fga, (char_u *)" = v:none");
962 }
963 ga_concat(&fga, (char_u *)")\nenddef\n");
964 ga_append(&fga, NUL);
965
966 exarg_T fea;
967 CLEAR_FIELD(fea);
968 fea.cmdidx = CMD_def;
969 fea.cmd = fea.arg = fga.ga_data;
970
971 garray_T lines_to_free;
972 ga_init2(&lines_to_free, sizeof(char_u *), 50);
973
974 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS);
975
976 ga_clear_strings(&lines_to_free);
977 vim_free(fga.ga_data);
978
979 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
980 {
981 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200982 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200983 ++classfunctions_gap->ga_len;
984
985 nf->uf_flags |= FC_NEW;
986 nf->uf_ret_type = get_type_ptr(type_list_gap);
987 if (nf->uf_ret_type != NULL)
988 {
989 nf->uf_ret_type->tt_type = VAR_OBJECT;
990 nf->uf_ret_type->tt_class = cl;
991 nf->uf_ret_type->tt_argcount = 0;
992 nf->uf_ret_type->tt_args = NULL;
993 }
994 }
995}
996
997/*
998 * Add the class functions and object methods to the new class "cl".
999 * When extending a class, add the functions and methods from the parent class
1000 * also.
1001 */
1002 static int
1003add_classfuncs_objmethods(
1004 class_T *cl,
1005 class_T *extends_cl,
1006 garray_T *classfunctions_gap,
1007 garray_T *objmethods_gap)
1008{
1009 // loop 1: class functions, loop 2: object methods
1010 for (int loop = 1; loop <= 2; ++loop)
1011 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001012 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1013 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001014 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001015 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001016 : &cl->class_obj_methods;
1017
1018 int parent_count = 0;
1019 if (extends_cl != NULL)
1020 // Include functions from the parent.
1021 parent_count = loop == 1
1022 ? extends_cl->class_class_function_count
1023 : extends_cl->class_obj_method_count;
1024
1025 *fcount = parent_count + gap->ga_len;
1026 if (*fcount == 0)
1027 {
1028 *fup = NULL;
1029 continue;
1030 }
1031 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1032 if (*fup == NULL)
1033 return FAIL;
1034
1035 if (gap->ga_len != 0)
1036 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
1037 vim_free(gap->ga_data);
1038 if (loop == 1)
1039 cl->class_class_function_count_child = gap->ga_len;
1040 else
1041 cl->class_obj_method_count_child = gap->ga_len;
1042
1043 int skipped = 0;
1044 for (int i = 0; i < parent_count; ++i)
1045 {
1046 // Copy functions from the parent. Can't use the same
1047 // function, because "uf_class" is different and compilation
1048 // will have a different result.
1049 // Put them after the functions in the current class, object
1050 // methods may be overruled, then "super.Method()" is used to
1051 // find a method from the parent.
1052 // Skip "new" functions. TODO: not all of them.
1053 if (loop == 1 && STRNCMP(
1054 extends_cl->class_class_functions[i]->uf_name,
1055 "new", 3) == 0)
1056 ++skipped;
1057 else
1058 {
1059 ufunc_T *pf = (loop == 1
1060 ? extends_cl->class_class_functions
1061 : extends_cl->class_obj_methods)[i];
1062 (*fup)[gap->ga_len + i - skipped] = copy_function(pf);
1063
1064 // If the child class overrides a function from the parent
1065 // the signature must be equal.
1066 char_u *pname = pf->uf_name;
1067 for (int ci = 0; ci < gap->ga_len; ++ci)
1068 {
1069 ufunc_T *cf = (*fup)[ci];
1070 char_u *cname = cf->uf_name;
1071 if (STRCMP(pname, cname) == 0)
1072 {
1073 where_T where = WHERE_INIT;
1074 where.wt_func_name = (char *)pname;
1075 where.wt_kind = WT_METHOD;
1076 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1077 TRUE, where);
1078 }
1079 }
1080 }
1081 }
1082
1083 *fcount -= skipped;
1084
1085 // Set the class pointer on all the functions and object methods.
1086 for (int i = 0; i < *fcount; ++i)
1087 {
1088 ufunc_T *fp = (*fup)[i];
1089 fp->uf_class = cl;
1090 if (loop == 2)
1091 fp->uf_flags |= FC_OBJECT;
1092 }
1093 }
1094
1095 return OK;
1096}
1097
1098/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001099 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001100 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001101 */
1102 void
1103ex_class(exarg_T *eap)
1104{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001105 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1106 long start_lnum = SOURCING_LNUM;
1107 char_u *arg = eap->arg;
1108 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001109
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001110 if (is_abstract)
1111 {
1112 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1113 {
1114 semsg(_(e_invalid_argument_str), arg);
1115 return;
1116 }
1117 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001118 is_class = TRUE;
1119 }
1120
1121 if (!current_script_is_vim9()
1122 || (cmdmod.cmod_flags & CMOD_LEGACY)
1123 || !getline_equal(eap->getline, eap->cookie, getsourceline))
1124 {
1125 if (is_class)
1126 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1127 else
1128 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1129 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001130 }
1131
1132 if (!ASCII_ISUPPER(*arg))
1133 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001134 if (is_class)
1135 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1136 else
1137 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1138 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001139 return;
1140 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001141 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1142 if (!IS_WHITE_OR_NUL(*name_end))
1143 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001144 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001145 return;
1146 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001147 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001148
Bram Moolenaara86655a2023-01-12 17:06:27 +00001149 // "export class" gets used when creating the class, don't use "is_export"
1150 // for the items inside the class.
1151 int class_export = is_export;
1152 is_export = FALSE;
1153
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001154 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001155 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001156
Bram Moolenaar83677162023-01-08 19:54:10 +00001157 // Name for "extends BaseClass"
1158 char_u *extends = NULL;
1159
Bram Moolenaar94674f22023-01-06 18:42:20 +00001160 // Names for "implements SomeInterface"
1161 garray_T ga_impl;
1162 ga_init2(&ga_impl, sizeof(char_u *), 5);
1163
1164 arg = skipwhite(name_end);
1165 while (*arg != NUL && *arg != '#' && *arg != '\n')
1166 {
1167 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001168 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001169 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1170 {
1171 if (extends != NULL)
1172 {
1173 emsg(_(e_duplicate_extends));
1174 goto early_ret;
1175 }
1176 arg = skipwhite(arg + 7);
1177 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1178 if (!IS_WHITE_OR_NUL(*end))
1179 {
1180 semsg(_(e_white_space_required_after_name_str), arg);
1181 goto early_ret;
1182 }
1183 extends = vim_strnsave(arg, end - arg);
1184 if (extends == NULL)
1185 goto early_ret;
1186
1187 arg = skipwhite(end + 1);
1188 }
1189 else if (STRNCMP(arg, "implements", 10) == 0
1190 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001191 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001192 if (ga_impl.ga_len > 0)
1193 {
1194 emsg(_(e_duplicate_implements));
1195 goto early_ret;
1196 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001197 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001198
1199 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001200 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001201 char_u *impl_end = find_name_end(arg, NULL, NULL,
1202 FNE_CHECK_START);
1203 if (!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1204 {
1205 semsg(_(e_white_space_required_after_name_str), arg);
1206 goto early_ret;
1207 }
1208 char_u *iname = vim_strnsave(arg, impl_end - arg);
1209 if (iname == NULL)
1210 goto early_ret;
1211 for (int i = 0; i < ga_impl.ga_len; ++i)
1212 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1213 {
1214 semsg(_(e_duplicate_interface_after_implements_str),
1215 iname);
1216 vim_free(iname);
1217 goto early_ret;
1218 }
1219 if (ga_add_string(&ga_impl, iname) == FAIL)
1220 {
1221 vim_free(iname);
1222 goto early_ret;
1223 }
1224 if (*impl_end != ',')
1225 {
1226 arg = skipwhite(impl_end);
1227 break;
1228 }
1229 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001230 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001231 }
1232 else
1233 {
1234 semsg(_(e_trailing_characters_str), arg);
1235early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001236 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001237 ga_clear_strings(&ga_impl);
1238 return;
1239 }
1240 }
1241
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001242 garray_T type_list; // list of pointers to allocated types
1243 ga_init2(&type_list, sizeof(type_T *), 10);
1244
Bram Moolenaard505d172022-12-18 21:42:55 +00001245 // Growarray with class members declared in the class.
1246 garray_T classmembers;
1247 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1248
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001249 // Growarray with functions declared in the class.
1250 garray_T classfunctions;
1251 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001252
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001253 // Growarray with object members declared in the class.
1254 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001255 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001256
1257 // Growarray with object methods declared in the class.
1258 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001259 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001260
1261 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001262 * Go over the body of the class/interface until "endclass" or
1263 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001264 */
1265 char_u *theline = NULL;
1266 int success = FALSE;
1267 for (;;)
1268 {
1269 vim_free(theline);
1270 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
1271 if (theline == NULL)
1272 break;
1273 char_u *line = skipwhite(theline);
1274
Bram Moolenaar418b5472022-12-20 13:38:22 +00001275 // Skip empty and comment lines.
1276 if (*line == NUL)
1277 continue;
1278 if (*line == '#')
1279 {
1280 if (vim9_bad_comment(line))
1281 break;
1282 continue;
1283 }
1284
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001285 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001286 char *end_name = is_class ? "endclass" : "endinterface";
1287 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001288 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001289 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001290 semsg(_(e_command_cannot_be_shortened_str), line);
1291 else if (*p == '|' || !ends_excmd2(line, p))
1292 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001293 else
1294 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001295 break;
1296 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001297 char *wrong_name = is_class ? "endinterface" : "endclass";
1298 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1299 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001300 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001301 break;
1302 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001303
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001304 int has_public = FALSE;
1305 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001306 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001307 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001308 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001309 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001310 break;
1311 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001312 has_public = TRUE;
1313 p = skipwhite(line + 6);
1314
Bram Moolenaard505d172022-12-18 21:42:55 +00001315 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001316 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001317 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001318 break;
1319 }
1320 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001321
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001322 int abstract_method = FALSE;
1323 char_u *pa = p;
1324 if (checkforcmd(&p, "abstract", 3))
1325 {
1326 if (STRNCMP(pa, "abstract", 8) != 0)
1327 {
1328 semsg(_(e_command_cannot_be_shortened_str), pa);
1329 break;
1330 }
1331
1332 if (!is_abstract)
1333 {
1334 semsg(_(e_abstract_method_in_concrete_class), pa);
1335 break;
1336 }
1337
1338 abstract_method = TRUE;
1339 p = skipwhite(pa + 8);
1340 if (STRNCMP(p, "def", 3) != 0 && STRNCMP(p, "static", 6) != 0)
1341 {
1342 emsg(_(e_abstract_must_be_followed_by_def_or_static));
1343 break;
1344 }
1345 }
1346
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001347 int has_static = FALSE;
1348 char_u *ps = p;
1349 if (checkforcmd(&p, "static", 4))
1350 {
1351 if (STRNCMP(ps, "static", 6) != 0)
1352 {
1353 semsg(_(e_command_cannot_be_shortened_str), ps);
1354 break;
1355 }
1356 has_static = TRUE;
1357 p = skipwhite(ps + 6);
1358 }
1359
Bram Moolenaard505d172022-12-18 21:42:55 +00001360 // object members (public, read access, private):
1361 // "this._varname"
1362 // "this.varname"
1363 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001364 if (STRNCMP(p, "this", 4) == 0)
1365 {
1366 if (p[4] != '.' || !eval_isnamec1(p[5]))
1367 {
1368 semsg(_(e_invalid_object_member_declaration_str), p);
1369 break;
1370 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001371 if (has_static)
1372 {
1373 emsg(_(e_static_cannot_be_followed_by_this));
1374 break;
1375 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001376 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +00001377 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001378 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001379 char_u *init_expr = NULL;
1380 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001381 &varname_end, &type_list, &type,
1382 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001383 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001384 if (is_duplicate_member(&objmembers, varname, varname_end))
1385 {
1386 vim_free(init_expr);
1387 break;
1388 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001389 if (add_member(&objmembers, varname, varname_end,
1390 has_public, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001391 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001392 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001393 break;
1394 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001395 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001396
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001397 // constructors:
1398 // def new()
1399 // enddef
1400 // def newOther()
1401 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001402 // object methods and class functions:
1403 // def SomeMethod()
1404 // enddef
1405 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001406 // enddef
1407 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001408 // def <Tval> someMethod()
1409 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001410 else if (checkforcmd(&p, "def", 3))
1411 {
1412 exarg_T ea;
1413 garray_T lines_to_free;
1414
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001415 // TODO: error for "public static def Func()"?
1416
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001417 CLEAR_FIELD(ea);
1418 ea.cmd = line;
1419 ea.arg = p;
1420 ea.cmdidx = CMD_def;
1421 ea.getline = eap->getline;
1422 ea.cookie = eap->cookie;
1423
1424 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001425 int class_flags;
1426 if (is_class)
1427 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
1428 else
1429 class_flags = CF_INTERFACE;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001430 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001431 class_flags);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001432 ga_clear_strings(&lines_to_free);
1433
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001434 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001435 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001436 char_u *name = uf->uf_name;
1437 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001438
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001439 if (is_new && !is_valid_constructor(uf, is_abstract,
1440 has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001441 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001442 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001443 break;
1444 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001445
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001446 garray_T *fgap = has_static || is_new
1447 ? &classfunctions : &objmethods;
Bram Moolenaar58b40092023-01-11 15:59:05 +00001448 // Check the name isn't used already.
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001449 if (is_duplicate_method(fgap, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001450 {
1451 success = FALSE;
1452 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001453 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001454 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001455
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001456 if (ga_grow(fgap, 1) == OK)
1457 {
1458 if (is_new)
1459 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001460
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001461 if (abstract_method)
1462 uf->uf_flags |= FC_ABSTRACT;
1463
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001464 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1465 ++fgap->ga_len;
1466 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001467 }
1468 }
1469
1470 // class members
1471 else if (has_static)
1472 {
1473 // class members (public, read access, private):
1474 // "static _varname"
1475 // "static varname"
1476 // "public static varname"
1477 char_u *varname = p;
1478 char_u *varname_end = NULL;
1479 type_T *type = NULL;
1480 char_u *init_expr = NULL;
1481 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001482 &varname_end, &type_list, &type,
1483 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001484 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001485 if (is_duplicate_member(&classmembers, varname, varname_end))
1486 {
1487 vim_free(init_expr);
1488 break;
1489 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001490 if (add_member(&classmembers, varname, varname_end,
1491 has_public, type, init_expr) == FAIL)
1492 {
1493 vim_free(init_expr);
1494 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001495 }
1496 }
1497
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001498 else
1499 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001500 if (is_class)
1501 semsg(_(e_not_valid_command_in_class_str), line);
1502 else
1503 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001504 break;
1505 }
1506 }
1507 vim_free(theline);
1508
Bram Moolenaar83677162023-01-08 19:54:10 +00001509 class_T *extends_cl = NULL; // class from "extends" argument
1510
1511 /*
1512 * Check a few things before defining the class.
1513 */
1514
1515 // Check the "extends" class is valid.
1516 if (success && extends != NULL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001517 success = validate_extends_class(extends, &extends_cl);
Bram Moolenaar83677162023-01-08 19:54:10 +00001518 VIM_CLEAR(extends);
1519
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001520 // Check the new class members and object members are not duplicates of the
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001521 // members in the extended class lineage.
1522 if (success && extends_cl != NULL)
1523 success = validate_extends_members(&classmembers, &objmembers,
1524 extends_cl);
1525
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001526 // When extending an abstract class, make sure all the abstract methods in
1527 // the parent class are implemented. If the current class is an abstract
1528 // class, then there is no need for this check.
1529 if (success && !is_abstract && extends_cl != NULL
1530 && (extends_cl->class_flags & CLASS_ABSTRACT))
1531 success = validate_extends_methods(&classfunctions, &objmethods,
1532 extends_cl);
1533
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001534 class_T **intf_classes = NULL;
1535
Bram Moolenaar83677162023-01-08 19:54:10 +00001536 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001537 if (success && ga_impl.ga_len > 0)
1538 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001539 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1540
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001541 success = validate_implements_classes(&ga_impl, intf_classes,
1542 &classfunctions, &classmembers,
1543 &objmethods, &objmembers);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001544 }
1545
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001546 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001547 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001548 success = check_func_arg_names(&classfunctions, &objmethods,
1549 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001550
Bram Moolenaareb533502022-12-14 15:06:11 +00001551 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001552 if (success)
1553 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001554 // "endclass" encountered without failures: Create the class.
1555
Bram Moolenaareb533502022-12-14 15:06:11 +00001556 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001557 if (cl == NULL)
1558 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001559 if (!is_class)
1560 cl->class_flags = CLASS_INTERFACE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001561 else if (is_abstract)
1562 cl->class_flags = CLASS_ABSTRACT;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001563
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001564 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001565 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001566 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001567 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001568
Bram Moolenaard0200c82023-01-28 15:19:40 +00001569 if (extends_cl != NULL)
1570 {
1571 cl->class_extends = extends_cl;
1572 extends_cl->class_flags |= CLASS_EXTENDED;
1573 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001574
Bram Moolenaard505d172022-12-18 21:42:55 +00001575 // Add class and object members to "cl".
1576 if (add_members_to_class(&classmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001577 extends_cl == NULL ? NULL
1578 : extends_cl->class_class_members,
1579 extends_cl == NULL ? 0
1580 : extends_cl->class_class_member_count,
1581 &cl->class_class_members,
1582 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001583 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001584 extends_cl == NULL ? NULL
1585 : extends_cl->class_obj_members,
1586 extends_cl == NULL ? 0
1587 : extends_cl->class_obj_member_count,
1588 &cl->class_obj_members,
1589 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001590 goto cleanup;
1591
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001592 if (ga_impl.ga_len > 0)
1593 {
1594 // Move the "implements" names into the class.
1595 cl->class_interface_count = ga_impl.ga_len;
1596 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1597 if (cl->class_interfaces == NULL)
1598 goto cleanup;
1599 for (int i = 0; i < ga_impl.ga_len; ++i)
1600 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1601 VIM_CLEAR(ga_impl.ga_data);
1602 ga_impl.ga_len = 0;
1603
Bram Moolenaard0200c82023-01-28 15:19:40 +00001604 cl->class_interfaces_cl = intf_classes;
1605 intf_classes = NULL;
1606 }
1607
1608 if (cl->class_interface_count > 0 || extends_cl != NULL)
1609 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001610 // Add a method and member lookup table to each of the interface
1611 // classes.
1612 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1613 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001614 }
1615
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001616 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001617 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001618 add_class_members(cl, eap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001619
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001620 int have_new = FALSE;
1621 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001622 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001623 {
1624 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1625 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001626 {
1627 have_new = TRUE;
1628 break;
1629 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001630 }
1631
1632 if (have_new)
1633 // The return type of new() is an object of class "cl"
1634 class_func->uf_ret_type->tt_class = cl;
1635 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001636 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001637 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001638
Bram Moolenaar58b40092023-01-11 15:59:05 +00001639 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001640 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1641 &objmethods) == FAIL)
1642 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001643
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001644 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001645 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001646 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001647 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001648 cl->class_type_list = type_list;
1649
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001650 class_created(cl);
1651
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001652 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001653 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001654
1655 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001656 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02001657 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001658 typval_T tv;
1659 tv.v_type = VAR_CLASS;
1660 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001661 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001662 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001663 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001664 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001665 return;
1666 }
1667
1668cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00001669 if (cl != NULL)
1670 {
1671 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001672 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001673 if (cl->class_interfaces != NULL)
1674 {
1675 for (int i = 0; i < cl->class_interface_count; ++i)
1676 vim_free(cl->class_interfaces[i]);
1677 vim_free(cl->class_interfaces);
1678 }
1679 if (cl->class_interfaces_cl != NULL)
1680 {
1681 for (int i = 0; i < cl->class_interface_count; ++i)
1682 class_unref(cl->class_interfaces_cl[i]);
1683 vim_free(cl->class_interfaces_cl);
1684 }
Bram Moolenaareb533502022-12-14 15:06:11 +00001685 vim_free(cl->class_obj_members);
1686 vim_free(cl->class_obj_methods);
1687 vim_free(cl);
1688 }
1689
Bram Moolenaar83677162023-01-08 19:54:10 +00001690 vim_free(extends);
1691 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001692
1693 if (intf_classes != NULL)
1694 {
1695 for (int i = 0; i < ga_impl.ga_len; ++i)
1696 class_unref(intf_classes[i]);
1697 vim_free(intf_classes);
1698 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001699 ga_clear_strings(&ga_impl);
1700
Bram Moolenaard505d172022-12-18 21:42:55 +00001701 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001702 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001703 garray_T *gap = round == 1 ? &classmembers : &objmembers;
1704 if (gap->ga_len == 0 || gap->ga_data == NULL)
1705 continue;
1706
1707 for (int i = 0; i < gap->ga_len; ++i)
1708 {
1709 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
1710 vim_free(m->ocm_name);
1711 vim_free(m->ocm_init);
1712 }
1713 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001714 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001715
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001716 for (int i = 0; i < objmethods.ga_len; ++i)
1717 {
1718 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
1719 func_clear_free(uf, FALSE);
1720 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001721 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001722
1723 for (int i = 0; i < classfunctions.ga_len; ++i)
1724 {
1725 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
1726 func_clear_free(uf, FALSE);
1727 }
1728 ga_clear(&classfunctions);
1729
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001730 clear_type_list(&type_list);
1731}
1732
1733/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001734 * Find member "name" in class "cl", set "member_idx" to the member index and
1735 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001736 * When "is_object" is TRUE, then look for object members. Otherwise look for
1737 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001738 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02001739 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001740 */
1741 type_T *
1742class_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001743 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001744 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001745 char_u *name,
1746 char_u *name_end,
1747 int *member_idx,
Ernie Rael456ae552023-09-01 18:54:54 +02001748 ocmember_T **p_m)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001749{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001750 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001751 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001752
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001753 *member_idx = -1; // not found (yet)
1754
1755 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
1756 member_idx);
1757 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001758 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001759 semsg(_(e_unknown_variable_str), name);
1760 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001761 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001762
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001763 if (p_m != NULL)
1764 *p_m = m;
1765
1766 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001767}
1768
1769/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001770 * Handle ":enum" up to ":endenum".
1771 */
1772 void
1773ex_enum(exarg_T *eap UNUSED)
1774{
1775 // TODO
1776}
1777
1778/*
1779 * Handle ":type".
1780 */
1781 void
1782ex_type(exarg_T *eap UNUSED)
1783{
1784 // TODO
1785}
1786
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001787/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001788 * Returns OK if a member variable named "name" is present in the class "cl".
1789 * Otherwise returns FAIL. If found, the member variable typval is set in
1790 * "rettv". If "is_object" is TRUE, then the object member variable table is
1791 * searched. Otherwise the class member variable table is searched.
1792 */
1793 static int
1794get_member_tv(
1795 class_T *cl,
1796 int is_object,
1797 char_u *name,
1798 size_t namelen,
1799 typval_T *rettv)
1800{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001801 ocmember_T *m;
1802 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001803
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001804 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
1805 &m_idx);
1806 if (m == NULL)
1807 return FAIL;
1808
1809 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001810 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001811 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
1812 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001813 }
1814
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001815 // The object only contains a pointer to the class, the member
1816 // values array follows right after that.
1817 object_T *obj = rettv->vval.v_object;
1818 if (is_object)
1819 {
1820 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
1821 copy_tv(tv, rettv);
1822 }
1823 else
1824 copy_tv(&cl->class_members_tv[m_idx], rettv);
1825
1826 object_unref(obj);
1827
1828 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001829}
1830
1831/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001832 * Evaluate what comes after a class:
1833 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001834 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001835 * - class constructor: SomeClass.new()
1836 * - object member: someObject.varname
1837 * - object method: someObject.SomeMethod()
1838 *
1839 * "*arg" points to the '.'.
1840 * "*arg" is advanced to after the member name or method call.
1841 *
1842 * Returns FAIL or OK.
1843 */
1844 int
1845class_object_index(
1846 char_u **arg,
1847 typval_T *rettv,
1848 evalarg_T *evalarg,
1849 int verbose UNUSED) // give error messages
1850{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001851 if (VIM_ISWHITE((*arg)[1]))
1852 {
1853 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
1854 return FAIL;
1855 }
1856
1857 ++*arg;
1858 char_u *name = *arg;
1859 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
1860 if (name_end == name)
1861 return FAIL;
1862 size_t len = name_end - name;
1863
Bram Moolenaar552bdca2023-02-17 21:08:50 +00001864 class_T *cl;
1865 if (rettv->v_type == VAR_CLASS)
1866 cl = rettv->vval.v_class;
1867 else // VAR_OBJECT
1868 {
1869 if (rettv->vval.v_object == NULL)
1870 {
1871 emsg(_(e_using_null_object));
1872 return FAIL;
1873 }
1874 cl = rettv->vval.v_object->obj_class;
1875 }
1876
Bram Moolenaard13dd302023-03-11 20:56:35 +00001877 if (cl == NULL)
1878 {
1879 emsg(_(e_incomplete_type));
1880 return FAIL;
1881 }
1882
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001883 if (*name_end == '(')
1884 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001885 ufunc_T *fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001886
Ernie Rael4d00b832023-09-11 19:54:42 +02001887 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001888 if (fp == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001889 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001890 semsg(_(e_method_not_found_on_class_str_str), cl->class_name,
1891 name);
1892 return FAIL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001893 }
1894
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001895 typval_T argvars[MAX_FUNC_ARGS + 1];
1896 int argcount = 0;
1897
1898 if (*fp->uf_name == '_')
1899 {
1900 // Cannot access a private method outside of a class
1901 semsg(_(e_cannot_access_private_method_str), name);
1902 return FAIL;
1903 }
1904
1905 char_u *argp = name_end;
1906 int ret = get_func_arguments(&argp, evalarg, 0,
1907 argvars, &argcount);
1908 if (ret == FAIL)
1909 return FAIL;
1910
1911 funcexe_T funcexe;
1912 CLEAR_FIELD(funcexe);
1913 funcexe.fe_evaluate = TRUE;
1914 if (rettv->v_type == VAR_OBJECT)
1915 {
1916 funcexe.fe_object = rettv->vval.v_object;
1917 ++funcexe.fe_object->obj_refcount;
1918 }
1919
1920 // Clear the class or object after calling the function, in
1921 // case the refcount is one.
1922 typval_T tv_tofree = *rettv;
1923 rettv->v_type = VAR_UNKNOWN;
1924
1925 // Call the user function. Result goes into rettv;
1926 int error = call_user_func_check(fp, argcount, argvars,
1927 rettv, &funcexe, NULL);
1928
1929 // Clear the previous rettv and the arguments.
1930 clear_tv(&tv_tofree);
1931 for (int idx = 0; idx < argcount; ++idx)
1932 clear_tv(&argvars[idx]);
1933
1934 if (error != FCERR_NONE)
1935 {
1936 user_func_error(error, printable_func_name(fp),
1937 funcexe.fe_found_var);
1938 return FAIL;
1939 }
1940 *arg = argp;
1941 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001942 }
1943
1944 else if (rettv->v_type == VAR_OBJECT)
1945 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001946 // Search in the object member variable table and the class member
1947 // variable table.
Yegappan Lakshmanan23c92d92023-09-09 11:33:29 +02001948 if (get_member_tv(cl, TRUE, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001949 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001950 *arg = name_end;
1951 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001952 }
1953
1954 semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
1955 }
1956
Bram Moolenaard505d172022-12-18 21:42:55 +00001957 else if (rettv->v_type == VAR_CLASS)
1958 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001959 int m_idx;
1960
Bram Moolenaard505d172022-12-18 21:42:55 +00001961 // class member
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001962 ocmember_T *m = class_member_lookup(cl, name, len, &m_idx);
1963 if (m == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001964 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001965 semsg(_(e_member_not_found_on_class_str_str), cl->class_name, name);
1966 return FAIL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001967 }
1968
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001969 if (*name == '_')
1970 {
1971 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
1972 return FAIL;
1973 }
1974 if ((cl->class_flags & CLASS_INTERFACE) != 0)
1975 {
1976 semsg(_(e_interface_static_direct_access_str),
1977 cl->class_name, m->ocm_name);
1978 return FAIL;
1979 }
1980
1981 typval_T *tv = &cl->class_members_tv[m_idx];
1982 copy_tv(tv, rettv);
1983 class_unref(cl);
1984
1985 *arg = name_end;
1986 return OK;
Bram Moolenaard505d172022-12-18 21:42:55 +00001987 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001988
1989 return FAIL;
1990}
1991
1992/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001993 * If "arg" points to a class or object method, return it.
1994 * Otherwise return NULL.
1995 */
1996 ufunc_T *
1997find_class_func(char_u **arg)
1998{
1999 char_u *name = *arg;
2000 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2001 if (name_end == name || *name_end != '.')
2002 return NULL;
2003
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002004 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002005 size_t len = name_end - name;
2006 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002007 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00002008 if (eval_variable(name, (int)len,
2009 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002010 return NULL;
2011 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00002012 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002013
2014 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
2015 : tv.vval.v_object->obj_class;
2016 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002017 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002018 char_u *fname = name_end + 1;
2019 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
2020 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00002021 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002022 len = fname_end - fname;
2023
Ernie Rael4d00b832023-09-11 19:54:42 +02002024 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002025
Bram Moolenaareb533502022-12-14 15:06:11 +00002026fail_after_eval:
2027 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002028 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002029}
2030
2031/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002032 * Returns the index of class member variable "name" in the class "cl".
2033 * Returns -1, if the variable is not found.
2034 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002035 */
2036 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002037class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002038{
Ernie Rael4d00b832023-09-11 19:54:42 +02002039 int idx;
2040 class_member_lookup(cl, name, namelen, &idx);
2041 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002042}
2043
2044/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002045 * Returns a pointer to the class member variable "name" in the class "cl".
2046 * Returns NULL if the variable is not found.
2047 * The member variable index is set in "idx".
2048 */
2049 ocmember_T *
2050class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2051{
Ernie Rael4d00b832023-09-11 19:54:42 +02002052 ocmember_T *ret_m = NULL;
2053 int ret_idx = -1;
2054 for (int i = 0; i < cl->class_class_member_count; ++i)
2055 {
2056 ocmember_T *m = &cl->class_class_members[i];
2057 if (namelen)
2058 {
2059 if (STRNCMP(name, m->ocm_name, namelen) == 0
2060 && m->ocm_name[namelen] == NUL)
2061 {
2062 ret_m = m;
2063 ret_idx = i;
2064 break;
2065 }
2066 }
2067 else if (STRCMP(name, m->ocm_name) == 0)
2068 {
2069 ret_m = m;
2070 ret_idx = i;
2071 break;
2072 }
2073 }
2074 if (idx != NULL)
2075 *idx = ret_idx;
2076 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002077}
2078
2079/*
2080 * Returns the index of class method "name" in the class "cl".
2081 * Returns -1, if the method is not found.
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002082 */
2083 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002084class_method_idx(class_T *cl, char_u *name, size_t namelen)
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002085{
Ernie Rael4d00b832023-09-11 19:54:42 +02002086 int idx;
2087 class_method_lookup(cl, name, namelen, &idx);
2088 return idx;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002089}
2090
2091/*
2092 * Returns a pointer to the class method "name" in class "cl".
2093 * Returns NULL if the method is not found.
2094 * The method index is set in "idx".
2095 */
2096 ufunc_T *
2097class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2098{
Ernie Rael4d00b832023-09-11 19:54:42 +02002099 ufunc_T *ret_fp = NULL;
2100 int ret_idx = -1;
2101 for (int i = 0; i < cl->class_class_function_count; ++i)
2102 {
2103 ufunc_T *fp = cl->class_class_functions[i];
2104 char_u *ufname = (char_u *)fp->uf_name;
2105 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2106 {
2107 ret_fp = fp;
2108 ret_idx = i;
2109 break;
2110 }
2111 }
2112 if (idx != NULL)
2113 *idx = ret_idx;
2114 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002115}
2116
2117/*
2118 * Returns the index of object member variable "name" in the class "cl".
2119 * Returns -1, if the variable is not found.
2120 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
2121 */
2122 int
2123object_member_idx(class_T *cl, char_u *name, size_t namelen)
2124{
Ernie Rael4d00b832023-09-11 19:54:42 +02002125 int idx;
2126 object_member_lookup(cl, name, namelen, &idx);
2127 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002128}
2129
2130/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002131 * Returns a pointer to the object member variable "name" in the class "cl".
2132 * Returns NULL if the variable is not found.
2133 * The object member variable index is set in "idx".
2134 */
2135 ocmember_T *
2136object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2137{
Ernie Rael4d00b832023-09-11 19:54:42 +02002138 ocmember_T *ret_m = NULL;
2139 int ret_idx = -1;
2140 for (int i = 0; i < cl->class_obj_member_count; ++i)
2141 {
2142 ocmember_T *m = &cl->class_obj_members[i];
2143 if (namelen)
2144 {
2145 if (STRNCMP(name, m->ocm_name, namelen) == 0
2146 && m->ocm_name[namelen] == NUL)
2147 {
2148 ret_m = m;
2149 ret_idx = i;
2150 break;
2151 }
2152 }
2153 else if (STRCMP(name, m->ocm_name) == 0)
2154 {
2155 ret_m = m;
2156 ret_idx = i;
2157 break;
2158 }
2159 }
2160 if (idx != NULL)
2161 *idx = ret_idx;
2162 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002163}
2164
2165/*
2166 * Returns the index of object method "name" in the class "cl".
2167 * Returns -1, if the method is not found.
2168 */
2169 int
2170object_method_idx(class_T *cl, char_u *name, size_t namelen)
2171{
Ernie Rael4d00b832023-09-11 19:54:42 +02002172 int idx;
2173 object_method_lookup(cl, name, namelen, &idx);
2174 return idx;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002175}
2176
2177/*
2178 * Returns a pointer to the object method "name" in class "cl".
2179 * Returns NULL if the method is not found.
2180 * The object method index is set in "idx".
2181 */
2182 ufunc_T *
2183object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2184{
Ernie Rael4d00b832023-09-11 19:54:42 +02002185 ufunc_T *ret_fp = NULL;
2186 int ret_idx = -1;
2187 for (int i = 0; i < cl->class_obj_method_count; ++i)
2188 {
2189 ufunc_T *fp = cl->class_obj_methods[i];
2190 // Use a separate pointer to avoid that ASAN complains about
2191 // uf_name[] only being 4 characters.
2192 char_u *ufname = (char_u *)fp->uf_name;
2193 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2194 {
2195 ret_fp = fp;
2196 ret_idx = i;
2197 break;
2198 }
2199 }
2200 if (idx != NULL)
2201 *idx = ret_idx;
2202 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002203}
2204
2205/*
2206 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
2207 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
2208 * object member variable.
2209 *
2210 * Returns a pointer to the member variable structure if variable is found.
2211 * Otherwise returns NULL. The member variable index is set in "*idx".
2212 */
2213 ocmember_T *
2214member_lookup(
2215 class_T *cl,
2216 vartype_T v_type,
2217 char_u *name,
2218 size_t namelen,
2219 int *idx)
2220{
2221 if (v_type == VAR_CLASS)
2222 return class_member_lookup(cl, name, namelen, idx);
2223 else
2224 return object_member_lookup(cl, name, namelen, idx);
2225}
2226
2227/*
2228 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
2229 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
2230 *
2231 * Returns a pointer to the method structure if variable is found.
2232 * Otherwise returns NULL. The method variable index is set in "*idx".
2233 */
2234 ufunc_T *
2235method_lookup(
2236 class_T *cl,
2237 vartype_T v_type,
2238 char_u *name,
2239 size_t namelen,
2240 int *idx)
2241{
2242 if (v_type == VAR_CLASS)
2243 return class_method_lookup(cl, name, namelen, idx);
2244 else
2245 return object_method_lookup(cl, name, namelen, idx);
2246}
2247
2248/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00002249 * Return TRUE if current context "cctx_arg" is inside class "cl".
2250 * Return FALSE if not.
2251 */
2252 int
2253inside_class(cctx_T *cctx_arg, class_T *cl)
2254{
2255 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002256 if (cctx->ctx_ufunc != NULL
2257 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002258 return TRUE;
2259 return FALSE;
2260}
2261
2262/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002263 * Make a copy of an object.
2264 */
2265 void
2266copy_object(typval_T *from, typval_T *to)
2267{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002268 if (from->vval.v_object == NULL)
2269 to->vval.v_object = NULL;
2270 else
2271 {
2272 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002273 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002274 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002275}
2276
2277/*
2278 * Free an object.
2279 */
2280 static void
2281object_clear(object_T *obj)
2282{
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002283 // Avoid a recursive call, it can happen if "obj" has a circular reference.
2284 obj->obj_refcount = INT_MAX;
2285
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002286 class_T *cl = obj->obj_class;
2287
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002288 if (!cl)
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002289 return;
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002290
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002291 // the member values are just after the object structure
2292 typval_T *tv = (typval_T *)(obj + 1);
2293 for (int i = 0; i < cl->class_obj_member_count; ++i)
2294 clear_tv(tv + i);
2295
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002296 // Remove from the list headed by "first_object".
2297 object_cleared(obj);
2298
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002299 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002300 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002301}
2302
2303/*
2304 * Unreference an object.
2305 */
2306 void
2307object_unref(object_T *obj)
2308{
2309 if (obj != NULL && --obj->obj_refcount <= 0)
2310 object_clear(obj);
2311}
2312
2313/*
2314 * Make a copy of a class.
2315 */
2316 void
2317copy_class(typval_T *from, typval_T *to)
2318{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002319 if (from->vval.v_class == NULL)
2320 to->vval.v_class = NULL;
2321 else
2322 {
2323 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002324 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002325 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002326}
2327
2328/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002329 * Free the class "cl" and its contents.
2330 */
2331 static void
2332class_free(class_T *cl)
2333{
2334 // Freeing what the class contains may recursively come back here.
2335 // Clear "class_name" first, if it is NULL the class does not need to
2336 // be freed.
2337 VIM_CLEAR(cl->class_name);
2338
2339 class_unref(cl->class_extends);
2340
2341 for (int i = 0; i < cl->class_interface_count; ++i)
2342 {
2343 vim_free(((char_u **)cl->class_interfaces)[i]);
2344 if (cl->class_interfaces_cl[i] != NULL)
2345 class_unref(cl->class_interfaces_cl[i]);
2346 }
2347 vim_free(cl->class_interfaces);
2348 vim_free(cl->class_interfaces_cl);
2349
2350 itf2class_T *next;
2351 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2352 {
2353 next = i2c->i2c_next;
2354 vim_free(i2c);
2355 }
2356
2357 for (int i = 0; i < cl->class_class_member_count; ++i)
2358 {
2359 ocmember_T *m = &cl->class_class_members[i];
2360 vim_free(m->ocm_name);
2361 vim_free(m->ocm_init);
2362 if (cl->class_members_tv != NULL)
2363 clear_tv(&cl->class_members_tv[i]);
2364 }
2365 vim_free(cl->class_class_members);
2366 vim_free(cl->class_members_tv);
2367
2368 for (int i = 0; i < cl->class_obj_member_count; ++i)
2369 {
2370 ocmember_T *m = &cl->class_obj_members[i];
2371 vim_free(m->ocm_name);
2372 vim_free(m->ocm_init);
2373 }
2374 vim_free(cl->class_obj_members);
2375
2376 for (int i = 0; i < cl->class_class_function_count; ++i)
2377 {
2378 ufunc_T *uf = cl->class_class_functions[i];
2379 func_clear_free(uf, FALSE);
2380 }
2381 vim_free(cl->class_class_functions);
2382
2383 for (int i = 0; i < cl->class_obj_method_count; ++i)
2384 {
2385 ufunc_T *uf = cl->class_obj_methods[i];
2386 func_clear_free(uf, FALSE);
2387 }
2388 vim_free(cl->class_obj_methods);
2389
2390 clear_type_list(&cl->class_type_list);
2391
2392 class_cleared(cl);
2393
2394 vim_free(cl);
2395}
2396
2397/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002398 * Unreference a class. Free it when the reference count goes down to zero.
2399 */
2400 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002401class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002402{
Bram Moolenaard505d172022-12-18 21:42:55 +00002403 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002404 class_free(cl);
2405}
2406
2407/*
2408 * Go through the list of all classes and free items without "copyID".
2409 */
2410 int
2411class_free_nonref(int copyID)
2412{
2413 int did_free = FALSE;
2414
2415 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002416 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002417 next_nonref_class = cl->class_next_used;
2418 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002419 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002420 // Free the class and items it contains.
2421 class_free(cl);
2422 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002423 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002424 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002425
2426 next_nonref_class = NULL;
2427 return did_free;
2428}
2429
2430 int
2431set_ref_in_classes(int copyID)
2432{
2433 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
2434 set_ref_in_item_class(cl, copyID, NULL, NULL);
2435
2436 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002437}
2438
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002439static object_T *first_object = NULL;
2440
2441/*
2442 * Call this function when an object has been created. It will be added to the
2443 * list headed by "first_object".
2444 */
2445 void
2446object_created(object_T *obj)
2447{
2448 if (first_object != NULL)
2449 {
2450 obj->obj_next_used = first_object;
2451 first_object->obj_prev_used = obj;
2452 }
2453 first_object = obj;
2454}
2455
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002456static object_T *next_nonref_obj = NULL;
2457
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002458/*
2459 * Call this function when an object has been cleared and is about to be freed.
2460 * It is removed from the list headed by "first_object".
2461 */
2462 void
2463object_cleared(object_T *obj)
2464{
2465 if (obj->obj_next_used != NULL)
2466 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
2467 if (obj->obj_prev_used != NULL)
2468 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
2469 else if (first_object == obj)
2470 first_object = obj->obj_next_used;
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002471
2472 // update the next object to check if needed
2473 if (obj == next_nonref_obj)
2474 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002475}
2476
2477/*
2478 * Go through the list of all objects and free items without "copyID".
2479 */
2480 int
2481object_free_nonref(int copyID)
2482{
2483 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002484
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002485 for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002486 {
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002487 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002488 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
2489 {
2490 // Free the object and items it contains.
2491 object_clear(obj);
2492 did_free = TRUE;
2493 }
2494 }
2495
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002496 next_nonref_obj = NULL;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002497 return did_free;
2498}
2499
LemonBoyafe04662023-08-23 21:08:11 +02002500/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002501 * Return TRUE when the class "cl", its base class or one of the implemented
2502 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02002503 */
2504 int
2505class_instance_of(class_T *cl, class_T *other_cl)
2506{
2507 if (cl == other_cl)
2508 return TRUE;
2509
2510 // Recursively check the base classes.
2511 for (; cl != NULL; cl = cl->class_extends)
2512 {
2513 if (cl == other_cl)
2514 return TRUE;
2515 // Check the implemented interfaces.
2516 for (int i = cl->class_interface_count - 1; i >= 0; --i)
2517 if (cl->class_interfaces_cl[i] == other_cl)
2518 return TRUE;
2519 }
2520
2521 return FALSE;
2522}
2523
2524/*
2525 * "instanceof(object, classinfo)" function
2526 */
2527 void
2528f_instanceof(typval_T *argvars, typval_T *rettv)
2529{
2530 typval_T *object_tv = &argvars[0];
2531 typval_T *classinfo_tv = &argvars[1];
2532 listitem_T *li;
2533
2534 rettv->vval.v_number = VVAL_FALSE;
2535
2536 if (check_for_object_arg(argvars, 0) == FAIL
2537 || check_for_class_or_list_arg(argvars, 1) == FAIL)
2538 return;
2539
2540 if (classinfo_tv->v_type == VAR_LIST)
2541 {
2542 FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
2543 {
2544 if (li->li_tv.v_type != VAR_CLASS)
2545 {
2546 emsg(_(e_class_required));
2547 return;
2548 }
2549
2550 if (class_instance_of(object_tv->vval.v_object->obj_class,
2551 li->li_tv.vval.v_class) == TRUE)
2552 {
2553 rettv->vval.v_number = VVAL_TRUE;
2554 return;
2555 }
2556 }
2557 }
2558 else if (classinfo_tv->v_type == VAR_CLASS)
2559 {
2560 rettv->vval.v_number = class_instance_of(object_tv->vval.v_object->obj_class,
2561 classinfo_tv->vval.v_class);
2562 }
2563}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002564
2565#endif // FEAT_EVAL