blob: bf7d63295777b0d8c78a76ec13161ac1a30e3fab [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 Lakshmananc30a90d2023-09-15 20:14:55 +0200325 * Check method names in the parent class lineage to make sure the access is
326 * the same for overridden methods.
327 */
328 static int
329validate_extends_methods(
330 garray_T *objmethods_gap,
331 class_T *extends_cl)
332{
333 class_T *super = extends_cl;
334 int method_count = objmethods_gap->ga_len;
335 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
336
337 while (super != NULL)
338 {
339 int extends_method_count = super->class_obj_method_count_child;
340 if (extends_method_count == 0)
341 {
342 super = super->class_extends;
343 continue;
344 }
345
346 ufunc_T **extends_methods = super->class_obj_methods;
347
348 for (int i = 0; i < extends_method_count; i++)
349 {
350 char_u *pstr = extends_methods[i]->uf_name;
351 int extends_private = (*pstr == '_');
352 if (extends_private)
353 pstr++;
354
355 for (int j = 0; j < method_count; j++)
356 {
357 char_u *qstr = cl_fp[j]->uf_name;
358 int priv_method = (*qstr == '_');
359 if (priv_method)
360 qstr++;
361 if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
362 {
363 // Method access is different between the super class and
364 // the subclass
365 semsg(_(e_method_str_of_class_str_has_different_access),
366 cl_fp[j]->uf_name, super->class_name);
367 return FALSE;
368 }
369 }
370 }
371 super = super->class_extends;
372 }
373
374 return TRUE;
375}
376
377/*
378 * Check whether a object member variable in "objmembers_gap" is a duplicate of
379 * a member in any of the extended parent class lineage. Returns TRUE if there
380 * are no duplicates.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200381 */
382 static int
383validate_extends_members(
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200384 garray_T *objmembers_gap,
385 class_T *extends_cl)
386{
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200387 // loop == 1: check class members
388 // loop == 2: check object members
389 int member_count = objmembers_gap->ga_len;
390 if (member_count == 0)
391 return TRUE;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200392
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200393 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
394
395 // Validate each member variable
396 for (int c_i = 0; c_i < member_count; c_i++)
397 {
398 class_T *p_cl = extends_cl;
399 ocmember_T *c_m = members + c_i;
400 char_u *pstr = (*c_m->ocm_name == '_')
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200401 ? c_m->ocm_name + 1 : c_m->ocm_name;
402
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200403 // Check in all the parent classes in the lineage
404 while (p_cl != NULL)
405 {
406 int p_member_count = p_cl->class_obj_member_count;
407 if (p_member_count == 0)
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200408 {
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200409 p_cl = p_cl->class_extends;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200410 continue;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200411 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200412 ocmember_T *p_members = p_cl->class_obj_members;
413
414 // Compare against all the members in the parent class
415 for (int p_i = 0; p_i < p_member_count; p_i++)
416 {
417 ocmember_T *p_m = p_members + p_i;
418 char_u *qstr = (*p_m->ocm_name == '_')
419 ? p_m->ocm_name + 1 : p_m->ocm_name;
420 if (STRCMP(pstr, qstr) == 0)
421 {
422 semsg(_(e_duplicate_member_str), c_m->ocm_name);
423 return FALSE;
424 }
425 }
426
427 p_cl = p_cl->class_extends;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200428 }
429 }
430
431 return TRUE;
432}
433
434/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200435 * When extending an abstract class, check whether all the abstract methods in
436 * the parent class are implemented. Returns TRUE if all the methods are
437 * implemented.
438 */
439 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200440validate_abstract_class_methods(
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200441 garray_T *classmethods_gap,
442 garray_T *objmethods_gap,
443 class_T *extends_cl)
444{
445 for (int loop = 1; loop <= 2; ++loop)
446 {
447 // loop == 1: check class methods
448 // loop == 2: check object methods
449 int extends_method_count = loop == 1
450 ? extends_cl->class_class_function_count
451 : extends_cl->class_obj_method_count;
452 if (extends_method_count == 0)
453 continue;
454
455 ufunc_T **extends_methods = loop == 1
456 ? extends_cl->class_class_functions
457 : extends_cl->class_obj_methods;
458
459 int method_count = loop == 1 ? classmethods_gap->ga_len
460 : objmethods_gap->ga_len;
461 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
462 ? classmethods_gap->ga_data
463 : objmethods_gap->ga_data);
464
465 for (int i = 0; i < extends_method_count; i++)
466 {
467 ufunc_T *uf = extends_methods[i];
468 if ((uf->uf_flags & FC_ABSTRACT) == 0)
469 continue;
470
471 int method_found = FALSE;
472
473 for (int j = 0; j < method_count; j++)
474 {
475 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
476 {
477 method_found = TRUE;
478 break;
479 }
480 }
481
482 if (!method_found)
483 {
484 semsg(_(e_abstract_method_str_not_found), uf->uf_name);
485 return FALSE;
486 }
487 }
488 }
489
490 return TRUE;
491}
492
493/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200494 * Check the members of the interface class "ifcl" match the class members
495 * ("classmembers_gap") and object members ("objmembers_gap") of a class.
496 * Returns TRUE if the class and object member names are valid.
497 */
498 static int
499validate_interface_members(
500 char_u *intf_class_name,
501 class_T *ifcl,
502 garray_T *classmembers_gap,
503 garray_T *objmembers_gap)
504{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200505 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200506 {
507 // loop == 1: check class members
508 // loop == 2: check object members
509 int if_count = loop == 1 ? ifcl->class_class_member_count
510 : ifcl->class_obj_member_count;
511 if (if_count == 0)
512 continue;
513 ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
514 : ifcl->class_obj_members;
515 ocmember_T *cl_ms = (ocmember_T *)(loop == 1
516 ? classmembers_gap->ga_data
517 : objmembers_gap->ga_data);
518 int cl_count = loop == 1 ? classmembers_gap->ga_len
519 : objmembers_gap->ga_len;
520 for (int if_i = 0; if_i < if_count; ++if_i)
521 {
522 int cl_i;
523 for (cl_i = 0; cl_i < cl_count; ++cl_i)
524 {
525 ocmember_T *m = &cl_ms[cl_i];
526 where_T where = WHERE_INIT;
527
528 if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) != 0)
529 continue;
530
531 // Ensure the type is matching.
532 where.wt_func_name = (char *)m->ocm_name;
533 where.wt_kind = WT_MEMBER;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200534 if (check_type(if_ms[if_i].ocm_type, m->ocm_type, TRUE,
535 where) == FAIL)
536 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200537
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200538 if (if_ms[if_i].ocm_access != m->ocm_access)
539 {
540 semsg(_(e_member_str_of_interface_str_has_different_access),
541 if_ms[if_i].ocm_name, intf_class_name);
542 return FALSE;
543 }
544
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200545 break;
546 }
547 if (cl_i == cl_count)
548 {
549 semsg(_(e_member_str_of_interface_str_not_implemented),
550 if_ms[if_i].ocm_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200551 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200552 }
553 }
554 }
555
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200556 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200557}
558
559/*
560 * Check the functions/methods of the interface class "ifcl" match the class
561 * methods ("classfunctions_gap") and object functions ("objmemthods_gap") of a
562 * class.
563 * Returns TRUE if the class and object member names are valid.
564 */
565 static int
566validate_interface_methods(
567 char_u *intf_class_name,
568 class_T *ifcl,
569 garray_T *classfunctions_gap,
570 garray_T *objmethods_gap)
571{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200572 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200573 {
574 // loop == 1: check class functions
575 // loop == 2: check object methods
576 int if_count = loop == 1 ? ifcl->class_class_function_count
577 : ifcl->class_obj_method_count;
578 if (if_count == 0)
579 continue;
580 ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
581 : ifcl->class_obj_methods;
582 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
583 ? classfunctions_gap->ga_data
584 : objmethods_gap->ga_data);
585 int cl_count = loop == 1 ? classfunctions_gap->ga_len
586 : objmethods_gap->ga_len;
587 for (int if_i = 0; if_i < if_count; ++if_i)
588 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200589 char_u *if_name = if_fp[if_i]->uf_name;
590 int cl_i;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200591 for (cl_i = 0; cl_i < cl_count; ++cl_i)
592 {
593 char_u *cl_name = cl_fp[cl_i]->uf_name;
594 if (STRCMP(if_name, cl_name) == 0)
595 {
596 where_T where = WHERE_INIT;
597
598 // Ensure the type is matching.
599 where.wt_func_name = (char *)if_name;
600 where.wt_kind = WT_METHOD;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200601 if (check_type(if_fp[if_i]->uf_func_type,
602 cl_fp[cl_i]->uf_func_type, TRUE, where) == FAIL)
603 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200604 break;
605 }
606 }
607 if (cl_i == cl_count)
608 {
609 semsg(_(e_function_str_of_interface_str_not_implemented),
610 if_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200611 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200612 }
613 }
614 }
615
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200616 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200617}
618
619/*
620 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200621 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200622 * object methods and object members in the new class are in
623 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
624 * "objmembers_gap" respectively.
625 */
626 static int
627validate_implements_classes(
628 garray_T *impl_gap,
629 class_T **intf_classes,
630 garray_T *classfunctions_gap,
631 garray_T *classmembers_gap,
632 garray_T *objmethods_gap,
633 garray_T *objmembers_gap)
634{
635 int success = TRUE;
636
637 for (int i = 0; i < impl_gap->ga_len && success; ++i)
638 {
639 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
640 typval_T tv;
641 tv.v_type = VAR_UNKNOWN;
642 if (eval_variable_import(impl, &tv) == FAIL)
643 {
644 semsg(_(e_interface_name_not_found_str), impl);
645 success = FALSE;
646 break;
647 }
648
649 if (tv.v_type != VAR_CLASS
650 || tv.vval.v_class == NULL
651 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
652 {
653 semsg(_(e_not_valid_interface_str), impl);
654 success = FALSE;
655 clear_tv(&tv);
656 break;
657 }
658
659 class_T *ifcl = tv.vval.v_class;
660 intf_classes[i] = ifcl;
661 ++ifcl->class_refcount;
662
663 // check the members of the interface match the members of the class
664 success = validate_interface_members(impl, ifcl, classmembers_gap,
665 objmembers_gap);
666
667 // check the functions/methods of the interface match the
668 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200669 if (success)
670 success = validate_interface_methods(impl, ifcl,
671 classfunctions_gap, objmethods_gap);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200672 clear_tv(&tv);
673 }
674
675 return success;
676}
677
678/*
679 * Check no function argument name is used as a class member.
680 * (Object members are always accessed with "this." prefix, so no need
681 * to check them.)
682 */
683 static int
684check_func_arg_names(
685 garray_T *classfunctions_gap,
686 garray_T *objmethods_gap,
687 garray_T *classmembers_gap)
688{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200689 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200690 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200691 {
692 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
693
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200694 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200695 {
696 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
697
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200698 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200699 {
700 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
701 garray_T *mgap = classmembers_gap;
702
703 // Check all the class member names
704 for (int mi = 0; mi < mgap->ga_len; ++mi)
705 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200706 char_u *mname =
707 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200708 if (STRCMP(aname, mname) == 0)
709 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200710 if (uf->uf_script_ctx.sc_sid > 0)
711 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
712
713 semsg(_(e_argument_already_declared_in_class_str),
714 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200715
716 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200717 }
718 }
719 }
720 }
721 }
722
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200723 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200724}
725
726/*
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200727 * Returns TRUE if the member "varname" is already defined.
728 */
729 static int
730is_duplicate_member(garray_T *mgap, char_u *varname, char_u *varname_end)
731{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200732 char_u *name = vim_strnsave(varname, varname_end - varname);
733 char_u *pstr = (*name == '_') ? name + 1 : name;
734 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200735
736 for (int i = 0; i < mgap->ga_len; ++i)
737 {
738 ocmember_T *m = ((ocmember_T *)mgap->ga_data) + i;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200739 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1 : m->ocm_name;
740 if (STRCMP(pstr, qstr) == 0)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200741 {
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200742 semsg(_(e_duplicate_member_str), name);
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200743 dup = TRUE;
744 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200745 }
746 }
747
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200748 vim_free(name);
749 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200750}
751
752/*
753 * Returns TRUE if the method "name" is already defined.
754 */
755 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200756is_duplicate_method(
757 garray_T *classmethods_gap,
758 garray_T *objmethods_gap,
759 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200760{
761 char_u *pstr = (*name == '_') ? name + 1 : name;
762
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200763 // loop 1: class methods, loop 2: object methods
764 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200765 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200766 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
767 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200768 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200769 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
770 char_u *qstr = *n == '_' ? n + 1 : n;
771 if (STRCMP(pstr, qstr) == 0)
772 {
773 semsg(_(e_duplicate_function_str), name);
774 return TRUE;
775 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200776 }
777 }
778
779 return FALSE;
780}
781
782/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200783 * Returns TRUE if the constructor is valid.
784 */
785 static int
786is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
787{
788 // Constructors are not allowed in abstract classes.
789 if (is_abstract)
790 {
791 emsg(_(e_cannot_define_new_function_in_abstract_class));
792 return FALSE;
793 }
794 // A constructor is always static, no need to define it so.
795 if (has_static)
796 {
797 emsg(_(e_cannot_define_new_function_as_static));
798 return FALSE;
799 }
800 // A return type should not be specified for the new()
801 // constructor method.
802 if (uf->uf_ret_type->tt_type != VAR_VOID)
803 {
804 emsg(_(e_cannot_use_a_return_type_with_new));
805 return FALSE;
806 }
807 return TRUE;
808}
809
810/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200811 * Update the interface class lookup table for the member index on the
812 * interface to the member index in the class implementing the interface.
813 * And a lookup table for the object method index on the interface
814 * to the object method index in the class implementing the interface.
815 * This is also used for updating the lookup table for the extended class
816 * hierarchy.
817 */
818 static int
819update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200820 class_T *ifcl,
821 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200822 garray_T *objmethods,
823 int pobj_method_offset,
824 int is_interface)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200825{
826 if (ifcl == NULL)
827 return OK;
828
829 // Table for members.
830 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200831 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200832 if (if2cl == NULL)
833 return FAIL;
834 if2cl->i2c_next = ifcl->class_itf2class;
835 ifcl->class_itf2class = if2cl;
836 if2cl->i2c_class = cl;
837 if2cl->i2c_is_method = FALSE;
838
839 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
840 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
841 {
842 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200843 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200844 {
845 int *table = (int *)(if2cl + 1);
846 table[if_i] = cl_i;
847 break;
848 }
849 }
850
851 // Table for methods.
852 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200853 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200854 if (if2cl == NULL)
855 return FAIL;
856 if2cl->i2c_next = ifcl->class_itf2class;
857 ifcl->class_itf2class = if2cl;
858 if2cl->i2c_class = cl;
859 if2cl->i2c_is_method = TRUE;
860
861 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
862 {
863 int done = FALSE;
864 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
865 {
866 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200867 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200868 {
869 int *table = (int *)(if2cl + 1);
870 table[if_i] = cl_i;
871 done = TRUE;
872 break;
873 }
874 }
875
876 // extended class object method is not overridden by the child class.
877 // Keep the method declared in one of the parent classes in the
878 // lineage.
879 if (!done && !is_interface)
880 {
881 // If "ifcl" is not the immediate parent of "cl", then search in
882 // the intermediate parent classes.
883 if (cl->class_extends != ifcl)
884 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200885 class_T *parent = cl->class_extends;
886 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200887
888 while (!done && parent != NULL && parent != ifcl)
889 {
890
891 for (int cl_i = 0;
892 cl_i < parent->class_obj_method_count_child; ++cl_i)
893 {
894 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
895 parent->class_obj_methods[cl_i]->uf_name)
896 == 0)
897 {
898 int *table = (int *)(if2cl + 1);
899 table[if_i] = method_offset + cl_i;
900 done = TRUE;
901 break;
902 }
903 }
904 method_offset += parent->class_obj_method_count_child;
905 parent = parent->class_extends;
906 }
907 }
908
909 if (!done)
910 {
911 int *table = (int *)(if2cl + 1);
912 table[if_i] = pobj_method_offset + if_i;
913 }
914 }
915 }
916
917 return OK;
918}
919
920/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200921 * Update the member and object method lookup tables for a new class in the
922 * interface class.
923 * For each interface add a lookup table for the member index on the interface
924 * to the member index in the new class. And a lookup table for the object
925 * method index on the interface to the object method index in the new class.
926 */
927 static int
928add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
929{
930 for (int i = 0; i < cl->class_interface_count; ++i)
931 {
932 class_T *ifcl = cl->class_interfaces_cl[i];
933
934 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
935 0, TRUE) == FAIL)
936 return FAIL;
937 }
938
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200939 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200940 if (extends_cl != NULL)
941 {
942 class_T *pclass = extends_cl;
943 int pobj_method_offset = objmethods_gap->ga_len;
944
945 // Update the entire lineage of extended classes.
946 while (pclass != NULL)
947 {
948 if (update_member_method_lookup_table(pclass, cl,
949 objmethods_gap, pobj_method_offset, FALSE) == FAIL)
950 return FAIL;
951
952 pobj_method_offset += pclass->class_obj_method_count_child;
953 pclass = pclass->class_extends;
954 }
955 }
956
957 return OK;
958}
959
960/*
961 * Add class members to a new class. Allocate a typval for each class member
962 * and initialize it.
963 */
964 static void
965add_class_members(class_T *cl, exarg_T *eap)
966{
967 // Allocate a typval for each class member and initialize it.
968 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
969 cl->class_class_member_count);
970 if (cl->class_members_tv == NULL)
971 return;
972
973 for (int i = 0; i < cl->class_class_member_count; ++i)
974 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200975 ocmember_T *m = &cl->class_class_members[i];
976 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200977 if (m->ocm_init != NULL)
978 {
979 typval_T *etv = eval_expr(m->ocm_init, eap);
980 if (etv != NULL)
981 {
982 *tv = *etv;
983 vim_free(etv);
984 }
985 }
986 else
987 {
988 // TODO: proper default value
989 tv->v_type = m->ocm_type->tt_type;
990 tv->vval.v_string = NULL;
991 }
992 }
993}
994
995/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +0200996 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200997 */
998 static void
999add_default_constructor(
1000 class_T *cl,
1001 garray_T *classfunctions_gap,
1002 garray_T *type_list_gap)
1003{
1004 garray_T fga;
1005
1006 ga_init2(&fga, 1, 1000);
1007 ga_concat(&fga, (char_u *)"new(");
1008 for (int i = 0; i < cl->class_obj_member_count; ++i)
1009 {
1010 if (i > 0)
1011 ga_concat(&fga, (char_u *)", ");
1012 ga_concat(&fga, (char_u *)"this.");
1013 ocmember_T *m = cl->class_obj_members + i;
1014 ga_concat(&fga, (char_u *)m->ocm_name);
1015 ga_concat(&fga, (char_u *)" = v:none");
1016 }
1017 ga_concat(&fga, (char_u *)")\nenddef\n");
1018 ga_append(&fga, NUL);
1019
1020 exarg_T fea;
1021 CLEAR_FIELD(fea);
1022 fea.cmdidx = CMD_def;
1023 fea.cmd = fea.arg = fga.ga_data;
1024
1025 garray_T lines_to_free;
1026 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1027
1028 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS);
1029
1030 ga_clear_strings(&lines_to_free);
1031 vim_free(fga.ga_data);
1032
1033 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1034 {
1035 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001036 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001037 ++classfunctions_gap->ga_len;
1038
1039 nf->uf_flags |= FC_NEW;
1040 nf->uf_ret_type = get_type_ptr(type_list_gap);
1041 if (nf->uf_ret_type != NULL)
1042 {
1043 nf->uf_ret_type->tt_type = VAR_OBJECT;
1044 nf->uf_ret_type->tt_class = cl;
1045 nf->uf_ret_type->tt_argcount = 0;
1046 nf->uf_ret_type->tt_args = NULL;
1047 }
1048 }
1049}
1050
1051/*
1052 * Add the class functions and object methods to the new class "cl".
1053 * When extending a class, add the functions and methods from the parent class
1054 * also.
1055 */
1056 static int
1057add_classfuncs_objmethods(
1058 class_T *cl,
1059 class_T *extends_cl,
1060 garray_T *classfunctions_gap,
1061 garray_T *objmethods_gap)
1062{
1063 // loop 1: class functions, loop 2: object methods
1064 for (int loop = 1; loop <= 2; ++loop)
1065 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001066 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1067 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001068 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001069 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001070 : &cl->class_obj_methods;
1071
1072 int parent_count = 0;
1073 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001074 // Include object methods from the parent.
1075 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001076 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001077 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001078 : extends_cl->class_obj_method_count;
1079
1080 *fcount = parent_count + gap->ga_len;
1081 if (*fcount == 0)
1082 {
1083 *fup = NULL;
1084 continue;
1085 }
1086 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1087 if (*fup == NULL)
1088 return FAIL;
1089
1090 if (gap->ga_len != 0)
1091 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
1092 vim_free(gap->ga_data);
1093 if (loop == 1)
1094 cl->class_class_function_count_child = gap->ga_len;
1095 else
1096 cl->class_obj_method_count_child = gap->ga_len;
1097
1098 int skipped = 0;
1099 for (int i = 0; i < parent_count; ++i)
1100 {
1101 // Copy functions from the parent. Can't use the same
1102 // function, because "uf_class" is different and compilation
1103 // will have a different result.
1104 // Put them after the functions in the current class, object
1105 // methods may be overruled, then "super.Method()" is used to
1106 // find a method from the parent.
1107 // Skip "new" functions. TODO: not all of them.
1108 if (loop == 1 && STRNCMP(
1109 extends_cl->class_class_functions[i]->uf_name,
1110 "new", 3) == 0)
1111 ++skipped;
1112 else
1113 {
1114 ufunc_T *pf = (loop == 1
1115 ? extends_cl->class_class_functions
1116 : extends_cl->class_obj_methods)[i];
1117 (*fup)[gap->ga_len + i - skipped] = copy_function(pf);
1118
1119 // If the child class overrides a function from the parent
1120 // the signature must be equal.
1121 char_u *pname = pf->uf_name;
1122 for (int ci = 0; ci < gap->ga_len; ++ci)
1123 {
1124 ufunc_T *cf = (*fup)[ci];
1125 char_u *cname = cf->uf_name;
1126 if (STRCMP(pname, cname) == 0)
1127 {
1128 where_T where = WHERE_INIT;
1129 where.wt_func_name = (char *)pname;
1130 where.wt_kind = WT_METHOD;
1131 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1132 TRUE, where);
1133 }
1134 }
1135 }
1136 }
1137
1138 *fcount -= skipped;
1139
1140 // Set the class pointer on all the functions and object methods.
1141 for (int i = 0; i < *fcount; ++i)
1142 {
1143 ufunc_T *fp = (*fup)[i];
1144 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001145 if (i < gap->ga_len)
1146 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001147 if (loop == 2)
1148 fp->uf_flags |= FC_OBJECT;
1149 }
1150 }
1151
1152 return OK;
1153}
1154
1155/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001156 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001157 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001158 */
1159 void
1160ex_class(exarg_T *eap)
1161{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001162 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1163 long start_lnum = SOURCING_LNUM;
1164 char_u *arg = eap->arg;
1165 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001166
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001167 if (is_abstract)
1168 {
1169 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1170 {
1171 semsg(_(e_invalid_argument_str), arg);
1172 return;
1173 }
1174 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001175 is_class = TRUE;
1176 }
1177
1178 if (!current_script_is_vim9()
1179 || (cmdmod.cmod_flags & CMOD_LEGACY)
1180 || !getline_equal(eap->getline, eap->cookie, getsourceline))
1181 {
1182 if (is_class)
1183 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1184 else
1185 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1186 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001187 }
1188
1189 if (!ASCII_ISUPPER(*arg))
1190 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001191 if (is_class)
1192 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1193 else
1194 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1195 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001196 return;
1197 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001198 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1199 if (!IS_WHITE_OR_NUL(*name_end))
1200 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001201 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001202 return;
1203 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001204 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001205
Bram Moolenaara86655a2023-01-12 17:06:27 +00001206 // "export class" gets used when creating the class, don't use "is_export"
1207 // for the items inside the class.
1208 int class_export = is_export;
1209 is_export = FALSE;
1210
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001211 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001212 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001213
Bram Moolenaar83677162023-01-08 19:54:10 +00001214 // Name for "extends BaseClass"
1215 char_u *extends = NULL;
1216
Bram Moolenaar94674f22023-01-06 18:42:20 +00001217 // Names for "implements SomeInterface"
1218 garray_T ga_impl;
1219 ga_init2(&ga_impl, sizeof(char_u *), 5);
1220
1221 arg = skipwhite(name_end);
1222 while (*arg != NUL && *arg != '#' && *arg != '\n')
1223 {
1224 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001225 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001226 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1227 {
1228 if (extends != NULL)
1229 {
1230 emsg(_(e_duplicate_extends));
1231 goto early_ret;
1232 }
1233 arg = skipwhite(arg + 7);
1234 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1235 if (!IS_WHITE_OR_NUL(*end))
1236 {
1237 semsg(_(e_white_space_required_after_name_str), arg);
1238 goto early_ret;
1239 }
1240 extends = vim_strnsave(arg, end - arg);
1241 if (extends == NULL)
1242 goto early_ret;
1243
1244 arg = skipwhite(end + 1);
1245 }
1246 else if (STRNCMP(arg, "implements", 10) == 0
1247 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001248 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001249 if (ga_impl.ga_len > 0)
1250 {
1251 emsg(_(e_duplicate_implements));
1252 goto early_ret;
1253 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001254 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001255
1256 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001257 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001258 char_u *impl_end = find_name_end(arg, NULL, NULL,
1259 FNE_CHECK_START);
1260 if (!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1261 {
1262 semsg(_(e_white_space_required_after_name_str), arg);
1263 goto early_ret;
1264 }
1265 char_u *iname = vim_strnsave(arg, impl_end - arg);
1266 if (iname == NULL)
1267 goto early_ret;
1268 for (int i = 0; i < ga_impl.ga_len; ++i)
1269 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1270 {
1271 semsg(_(e_duplicate_interface_after_implements_str),
1272 iname);
1273 vim_free(iname);
1274 goto early_ret;
1275 }
1276 if (ga_add_string(&ga_impl, iname) == FAIL)
1277 {
1278 vim_free(iname);
1279 goto early_ret;
1280 }
1281 if (*impl_end != ',')
1282 {
1283 arg = skipwhite(impl_end);
1284 break;
1285 }
1286 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001287 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001288 }
1289 else
1290 {
1291 semsg(_(e_trailing_characters_str), arg);
1292early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001293 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001294 ga_clear_strings(&ga_impl);
1295 return;
1296 }
1297 }
1298
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001299 garray_T type_list; // list of pointers to allocated types
1300 ga_init2(&type_list, sizeof(type_T *), 10);
1301
Bram Moolenaard505d172022-12-18 21:42:55 +00001302 // Growarray with class members declared in the class.
1303 garray_T classmembers;
1304 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1305
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001306 // Growarray with functions declared in the class.
1307 garray_T classfunctions;
1308 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001309
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001310 // Growarray with object members declared in the class.
1311 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001312 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001313
1314 // Growarray with object methods declared in the class.
1315 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001316 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001317
1318 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001319 * Go over the body of the class/interface until "endclass" or
1320 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001321 */
1322 char_u *theline = NULL;
1323 int success = FALSE;
1324 for (;;)
1325 {
1326 vim_free(theline);
1327 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
1328 if (theline == NULL)
1329 break;
1330 char_u *line = skipwhite(theline);
1331
Bram Moolenaar418b5472022-12-20 13:38:22 +00001332 // Skip empty and comment lines.
1333 if (*line == NUL)
1334 continue;
1335 if (*line == '#')
1336 {
1337 if (vim9_bad_comment(line))
1338 break;
1339 continue;
1340 }
1341
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001342 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001343 char *end_name = is_class ? "endclass" : "endinterface";
1344 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001345 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001346 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001347 semsg(_(e_command_cannot_be_shortened_str), line);
1348 else if (*p == '|' || !ends_excmd2(line, p))
1349 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001350 else
1351 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001352 break;
1353 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001354 char *wrong_name = is_class ? "endinterface" : "endclass";
1355 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1356 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001357 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001358 break;
1359 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001360
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001361 int has_public = FALSE;
1362 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001363 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001364 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001365 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001366 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001367 break;
1368 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001369 has_public = TRUE;
1370 p = skipwhite(line + 6);
1371
Bram Moolenaard505d172022-12-18 21:42:55 +00001372 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001373 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001374 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001375 break;
1376 }
1377 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001378
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001379 int abstract_method = FALSE;
1380 char_u *pa = p;
1381 if (checkforcmd(&p, "abstract", 3))
1382 {
1383 if (STRNCMP(pa, "abstract", 8) != 0)
1384 {
1385 semsg(_(e_command_cannot_be_shortened_str), pa);
1386 break;
1387 }
1388
1389 if (!is_abstract)
1390 {
1391 semsg(_(e_abstract_method_in_concrete_class), pa);
1392 break;
1393 }
1394
1395 abstract_method = TRUE;
1396 p = skipwhite(pa + 8);
1397 if (STRNCMP(p, "def", 3) != 0 && STRNCMP(p, "static", 6) != 0)
1398 {
1399 emsg(_(e_abstract_must_be_followed_by_def_or_static));
1400 break;
1401 }
1402 }
1403
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001404 int has_static = FALSE;
1405 char_u *ps = p;
1406 if (checkforcmd(&p, "static", 4))
1407 {
1408 if (STRNCMP(ps, "static", 6) != 0)
1409 {
1410 semsg(_(e_command_cannot_be_shortened_str), ps);
1411 break;
1412 }
1413 has_static = TRUE;
1414 p = skipwhite(ps + 6);
1415 }
1416
Bram Moolenaard505d172022-12-18 21:42:55 +00001417 // object members (public, read access, private):
1418 // "this._varname"
1419 // "this.varname"
1420 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001421 if (STRNCMP(p, "this", 4) == 0)
1422 {
1423 if (p[4] != '.' || !eval_isnamec1(p[5]))
1424 {
1425 semsg(_(e_invalid_object_member_declaration_str), p);
1426 break;
1427 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001428 if (has_static)
1429 {
1430 emsg(_(e_static_cannot_be_followed_by_this));
1431 break;
1432 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001433 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +00001434 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001435 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001436 char_u *init_expr = NULL;
1437 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001438 &varname_end, &type_list, &type,
1439 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001440 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001441 if (is_duplicate_member(&objmembers, varname, varname_end))
1442 {
1443 vim_free(init_expr);
1444 break;
1445 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001446 if (add_member(&objmembers, varname, varname_end,
1447 has_public, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001448 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001449 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001450 break;
1451 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001452 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001453
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001454 // constructors:
1455 // def new()
1456 // enddef
1457 // def newOther()
1458 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001459 // object methods and class functions:
1460 // def SomeMethod()
1461 // enddef
1462 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001463 // enddef
1464 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001465 // def <Tval> someMethod()
1466 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001467 else if (checkforcmd(&p, "def", 3))
1468 {
1469 exarg_T ea;
1470 garray_T lines_to_free;
1471
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001472 // TODO: error for "public static def Func()"?
1473
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001474 CLEAR_FIELD(ea);
1475 ea.cmd = line;
1476 ea.arg = p;
1477 ea.cmdidx = CMD_def;
1478 ea.getline = eap->getline;
1479 ea.cookie = eap->cookie;
1480
1481 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001482 int class_flags;
1483 if (is_class)
1484 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
1485 else
1486 class_flags = CF_INTERFACE;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001487 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001488 class_flags);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001489 ga_clear_strings(&lines_to_free);
1490
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001491 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001492 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001493 char_u *name = uf->uf_name;
1494 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001495
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001496 if (is_new && !is_valid_constructor(uf, is_abstract,
1497 has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001498 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001499 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001500 break;
1501 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001502
Bram Moolenaar58b40092023-01-11 15:59:05 +00001503 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001504 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001505 {
1506 success = FALSE;
1507 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001508 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001509 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001510
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001511 garray_T *fgap = has_static || is_new
1512 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001513 if (ga_grow(fgap, 1) == OK)
1514 {
1515 if (is_new)
1516 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001517
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001518 if (abstract_method)
1519 uf->uf_flags |= FC_ABSTRACT;
1520
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001521 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1522 ++fgap->ga_len;
1523 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001524 }
1525 }
1526
1527 // class members
1528 else if (has_static)
1529 {
1530 // class members (public, read access, private):
1531 // "static _varname"
1532 // "static varname"
1533 // "public static varname"
1534 char_u *varname = p;
1535 char_u *varname_end = NULL;
1536 type_T *type = NULL;
1537 char_u *init_expr = NULL;
1538 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001539 &varname_end, &type_list, &type,
1540 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001541 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001542 if (is_duplicate_member(&classmembers, varname, varname_end))
1543 {
1544 vim_free(init_expr);
1545 break;
1546 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001547 if (add_member(&classmembers, varname, varname_end,
1548 has_public, type, init_expr) == FAIL)
1549 {
1550 vim_free(init_expr);
1551 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001552 }
1553 }
1554
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001555 else
1556 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001557 if (is_class)
1558 semsg(_(e_not_valid_command_in_class_str), line);
1559 else
1560 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001561 break;
1562 }
1563 }
1564 vim_free(theline);
1565
Bram Moolenaar83677162023-01-08 19:54:10 +00001566 class_T *extends_cl = NULL; // class from "extends" argument
1567
1568 /*
1569 * Check a few things before defining the class.
1570 */
1571
1572 // Check the "extends" class is valid.
1573 if (success && extends != NULL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001574 success = validate_extends_class(extends, &extends_cl);
Bram Moolenaar83677162023-01-08 19:54:10 +00001575 VIM_CLEAR(extends);
1576
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001577 // Check the new object methods to make sure their access (public or
1578 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001579 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001580 success = validate_extends_methods(&objmethods, extends_cl);
1581
1582 // Check the new class and object variables are not duplicates of the
1583 // variables in the extended class lineage.
1584 if (success && extends_cl != NULL)
1585 success = validate_extends_members(&objmembers, extends_cl);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001586
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001587 // When extending an abstract class, make sure all the abstract methods in
1588 // the parent class are implemented. If the current class is an abstract
1589 // class, then there is no need for this check.
1590 if (success && !is_abstract && extends_cl != NULL
1591 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001592 success = validate_abstract_class_methods(&classfunctions,
1593 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001594
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001595 class_T **intf_classes = NULL;
1596
Bram Moolenaar83677162023-01-08 19:54:10 +00001597 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001598 if (success && ga_impl.ga_len > 0)
1599 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001600 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1601
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001602 success = validate_implements_classes(&ga_impl, intf_classes,
1603 &classfunctions, &classmembers,
1604 &objmethods, &objmembers);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001605 }
1606
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001607 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001608 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001609 success = check_func_arg_names(&classfunctions, &objmethods,
1610 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001611
Bram Moolenaareb533502022-12-14 15:06:11 +00001612 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001613 if (success)
1614 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001615 // "endclass" encountered without failures: Create the class.
1616
Bram Moolenaareb533502022-12-14 15:06:11 +00001617 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001618 if (cl == NULL)
1619 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001620 if (!is_class)
1621 cl->class_flags = CLASS_INTERFACE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001622 else if (is_abstract)
1623 cl->class_flags = CLASS_ABSTRACT;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001624
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001625 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001626 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001627 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001628 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001629
Bram Moolenaard0200c82023-01-28 15:19:40 +00001630 if (extends_cl != NULL)
1631 {
1632 cl->class_extends = extends_cl;
1633 extends_cl->class_flags |= CLASS_EXTENDED;
1634 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001635
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001636 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00001637 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001638 NULL,
1639 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00001640 &cl->class_class_members,
1641 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001642 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001643 extends_cl == NULL ? NULL
1644 : extends_cl->class_obj_members,
1645 extends_cl == NULL ? 0
1646 : extends_cl->class_obj_member_count,
1647 &cl->class_obj_members,
1648 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001649 goto cleanup;
1650
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001651 if (ga_impl.ga_len > 0)
1652 {
1653 // Move the "implements" names into the class.
1654 cl->class_interface_count = ga_impl.ga_len;
1655 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1656 if (cl->class_interfaces == NULL)
1657 goto cleanup;
1658 for (int i = 0; i < ga_impl.ga_len; ++i)
1659 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1660 VIM_CLEAR(ga_impl.ga_data);
1661 ga_impl.ga_len = 0;
1662
Bram Moolenaard0200c82023-01-28 15:19:40 +00001663 cl->class_interfaces_cl = intf_classes;
1664 intf_classes = NULL;
1665 }
1666
1667 if (cl->class_interface_count > 0 || extends_cl != NULL)
1668 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001669 // Add a method and member lookup table to each of the interface
1670 // classes.
1671 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1672 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001673 }
1674
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001675 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001676 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001677 add_class_members(cl, eap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001678
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001679 int have_new = FALSE;
1680 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001681 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001682 {
1683 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1684 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001685 {
1686 have_new = TRUE;
1687 break;
1688 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001689 }
1690
1691 if (have_new)
1692 // The return type of new() is an object of class "cl"
1693 class_func->uf_ret_type->tt_class = cl;
1694 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001695 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001696 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001697
Bram Moolenaar58b40092023-01-11 15:59:05 +00001698 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001699 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1700 &objmethods) == FAIL)
1701 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001702
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001703 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001704 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001705 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001706 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001707 cl->class_type_list = type_list;
1708
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001709 class_created(cl);
1710
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001711 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001712 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001713
1714 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001715 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02001716 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001717 typval_T tv;
1718 tv.v_type = VAR_CLASS;
1719 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001720 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001721 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001722 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001723 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001724 return;
1725 }
1726
1727cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00001728 if (cl != NULL)
1729 {
1730 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001731 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001732 if (cl->class_interfaces != NULL)
1733 {
1734 for (int i = 0; i < cl->class_interface_count; ++i)
1735 vim_free(cl->class_interfaces[i]);
1736 vim_free(cl->class_interfaces);
1737 }
1738 if (cl->class_interfaces_cl != NULL)
1739 {
1740 for (int i = 0; i < cl->class_interface_count; ++i)
1741 class_unref(cl->class_interfaces_cl[i]);
1742 vim_free(cl->class_interfaces_cl);
1743 }
Bram Moolenaareb533502022-12-14 15:06:11 +00001744 vim_free(cl->class_obj_members);
1745 vim_free(cl->class_obj_methods);
1746 vim_free(cl);
1747 }
1748
Bram Moolenaar83677162023-01-08 19:54:10 +00001749 vim_free(extends);
1750 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001751
1752 if (intf_classes != NULL)
1753 {
1754 for (int i = 0; i < ga_impl.ga_len; ++i)
1755 class_unref(intf_classes[i]);
1756 vim_free(intf_classes);
1757 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001758 ga_clear_strings(&ga_impl);
1759
Bram Moolenaard505d172022-12-18 21:42:55 +00001760 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001761 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001762 garray_T *gap = round == 1 ? &classmembers : &objmembers;
1763 if (gap->ga_len == 0 || gap->ga_data == NULL)
1764 continue;
1765
1766 for (int i = 0; i < gap->ga_len; ++i)
1767 {
1768 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
1769 vim_free(m->ocm_name);
1770 vim_free(m->ocm_init);
1771 }
1772 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001773 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001774
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001775 for (int i = 0; i < objmethods.ga_len; ++i)
1776 {
1777 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
1778 func_clear_free(uf, FALSE);
1779 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001780 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001781
1782 for (int i = 0; i < classfunctions.ga_len; ++i)
1783 {
1784 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
1785 func_clear_free(uf, FALSE);
1786 }
1787 ga_clear(&classfunctions);
1788
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001789 clear_type_list(&type_list);
1790}
1791
1792/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001793 * Find member "name" in class "cl", set "member_idx" to the member index and
1794 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001795 * When "is_object" is TRUE, then look for object members. Otherwise look for
1796 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001797 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02001798 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001799 */
1800 type_T *
1801class_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001802 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001803 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001804 char_u *name,
1805 char_u *name_end,
1806 int *member_idx,
Ernie Rael456ae552023-09-01 18:54:54 +02001807 ocmember_T **p_m)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001808{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001809 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001810 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001811
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001812 *member_idx = -1; // not found (yet)
1813
1814 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
1815 member_idx);
1816 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001817 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001818 char_u *varname = vim_strnsave(name, len);
1819 if (varname != NULL)
1820 {
1821 if (is_object && class_member_idx(cl, name, len) >= 0)
1822 // A class variable with this name is present
1823 semsg(_(e_class_member_str_accessible_only_inside_class_str),
1824 varname, cl->class_name);
1825 else if (!is_object && object_member_idx(cl, name, len) >= 0)
1826 // An instance variable with this name is present
1827 semsg(_(e_object_member_str_accessible_only_using_object_str),
1828 varname, cl->class_name);
1829 else
1830 semsg(_(e_unknown_variable_str), varname);
1831 }
1832 vim_free(varname);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001833 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001834 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001835
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001836 if (p_m != NULL)
1837 *p_m = m;
1838
1839 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001840}
1841
1842/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001843 * Handle ":enum" up to ":endenum".
1844 */
1845 void
1846ex_enum(exarg_T *eap UNUSED)
1847{
1848 // TODO
1849}
1850
1851/*
1852 * Handle ":type".
1853 */
1854 void
1855ex_type(exarg_T *eap UNUSED)
1856{
1857 // TODO
1858}
1859
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001860/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001861 * Returns OK if a member variable named "name" is present in the class "cl".
1862 * Otherwise returns FAIL. If found, the member variable typval is set in
1863 * "rettv". If "is_object" is TRUE, then the object member variable table is
1864 * searched. Otherwise the class member variable table is searched.
1865 */
1866 static int
1867get_member_tv(
1868 class_T *cl,
1869 int is_object,
1870 char_u *name,
1871 size_t namelen,
1872 typval_T *rettv)
1873{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001874 ocmember_T *m;
1875 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001876
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001877 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
1878 &m_idx);
1879 if (m == NULL)
1880 return FAIL;
1881
1882 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001883 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001884 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
1885 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001886 }
1887
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001888 // The object only contains a pointer to the class, the member
1889 // values array follows right after that.
1890 object_T *obj = rettv->vval.v_object;
1891 if (is_object)
1892 {
1893 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
1894 copy_tv(tv, rettv);
1895 }
1896 else
1897 copy_tv(&cl->class_members_tv[m_idx], rettv);
1898
1899 object_unref(obj);
1900
1901 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001902}
1903
1904/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001905 * Evaluate what comes after a class:
1906 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001907 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001908 * - class constructor: SomeClass.new()
1909 * - object member: someObject.varname
1910 * - object method: someObject.SomeMethod()
1911 *
1912 * "*arg" points to the '.'.
1913 * "*arg" is advanced to after the member name or method call.
1914 *
1915 * Returns FAIL or OK.
1916 */
1917 int
1918class_object_index(
1919 char_u **arg,
1920 typval_T *rettv,
1921 evalarg_T *evalarg,
1922 int verbose UNUSED) // give error messages
1923{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001924 if (VIM_ISWHITE((*arg)[1]))
1925 {
1926 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
1927 return FAIL;
1928 }
1929
1930 ++*arg;
1931 char_u *name = *arg;
1932 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
1933 if (name_end == name)
1934 return FAIL;
1935 size_t len = name_end - name;
1936
Bram Moolenaar552bdca2023-02-17 21:08:50 +00001937 class_T *cl;
1938 if (rettv->v_type == VAR_CLASS)
1939 cl = rettv->vval.v_class;
1940 else // VAR_OBJECT
1941 {
1942 if (rettv->vval.v_object == NULL)
1943 {
1944 emsg(_(e_using_null_object));
1945 return FAIL;
1946 }
1947 cl = rettv->vval.v_object->obj_class;
1948 }
1949
Bram Moolenaard13dd302023-03-11 20:56:35 +00001950 if (cl == NULL)
1951 {
1952 emsg(_(e_incomplete_type));
1953 return FAIL;
1954 }
1955
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001956 if (*name_end == '(')
1957 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001958 ufunc_T *fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001959
Ernie Rael4d00b832023-09-11 19:54:42 +02001960 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001961 if (fp == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001962 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001963 method_not_found_msg(cl, rettv->v_type, name, len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001964 return FAIL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001965 }
1966
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001967 typval_T argvars[MAX_FUNC_ARGS + 1];
1968 int argcount = 0;
1969
1970 if (*fp->uf_name == '_')
1971 {
1972 // Cannot access a private method outside of a class
1973 semsg(_(e_cannot_access_private_method_str), name);
1974 return FAIL;
1975 }
1976
1977 char_u *argp = name_end;
1978 int ret = get_func_arguments(&argp, evalarg, 0,
1979 argvars, &argcount);
1980 if (ret == FAIL)
1981 return FAIL;
1982
1983 funcexe_T funcexe;
1984 CLEAR_FIELD(funcexe);
1985 funcexe.fe_evaluate = TRUE;
1986 if (rettv->v_type == VAR_OBJECT)
1987 {
1988 funcexe.fe_object = rettv->vval.v_object;
1989 ++funcexe.fe_object->obj_refcount;
1990 }
1991
1992 // Clear the class or object after calling the function, in
1993 // case the refcount is one.
1994 typval_T tv_tofree = *rettv;
1995 rettv->v_type = VAR_UNKNOWN;
1996
1997 // Call the user function. Result goes into rettv;
1998 int error = call_user_func_check(fp, argcount, argvars,
1999 rettv, &funcexe, NULL);
2000
2001 // Clear the previous rettv and the arguments.
2002 clear_tv(&tv_tofree);
2003 for (int idx = 0; idx < argcount; ++idx)
2004 clear_tv(&argvars[idx]);
2005
2006 if (error != FCERR_NONE)
2007 {
2008 user_func_error(error, printable_func_name(fp),
2009 funcexe.fe_found_var);
2010 return FAIL;
2011 }
2012 *arg = argp;
2013 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002014 }
2015
2016 else if (rettv->v_type == VAR_OBJECT)
2017 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002018 // Search in the object member variable table and the class member
2019 // variable table.
Yegappan Lakshmanan23c92d92023-09-09 11:33:29 +02002020 if (get_member_tv(cl, TRUE, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002021 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002022 *arg = name_end;
2023 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002024 }
2025
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002026 member_not_found_msg(cl, VAR_OBJECT, name, len);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002027 }
2028
Bram Moolenaard505d172022-12-18 21:42:55 +00002029 else if (rettv->v_type == VAR_CLASS)
2030 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002031 int m_idx;
2032
Bram Moolenaard505d172022-12-18 21:42:55 +00002033 // class member
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002034 ocmember_T *m = class_member_lookup(cl, name, len, &m_idx);
2035 if (m == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002036 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002037 member_not_found_msg(cl, VAR_CLASS, name, len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002038 return FAIL;
Bram Moolenaard505d172022-12-18 21:42:55 +00002039 }
2040
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002041 if (*name == '_')
2042 {
2043 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
2044 return FAIL;
2045 }
2046 if ((cl->class_flags & CLASS_INTERFACE) != 0)
2047 {
2048 semsg(_(e_interface_static_direct_access_str),
2049 cl->class_name, m->ocm_name);
2050 return FAIL;
2051 }
2052
2053 typval_T *tv = &cl->class_members_tv[m_idx];
2054 copy_tv(tv, rettv);
2055 class_unref(cl);
2056
2057 *arg = name_end;
2058 return OK;
Bram Moolenaard505d172022-12-18 21:42:55 +00002059 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002060
2061 return FAIL;
2062}
2063
2064/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002065 * If "arg" points to a class or object method, return it.
2066 * Otherwise return NULL.
2067 */
2068 ufunc_T *
2069find_class_func(char_u **arg)
2070{
2071 char_u *name = *arg;
2072 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2073 if (name_end == name || *name_end != '.')
2074 return NULL;
2075
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002076 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002077 size_t len = name_end - name;
2078 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002079 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00002080 if (eval_variable(name, (int)len,
2081 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002082 return NULL;
2083 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00002084 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002085
2086 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
2087 : tv.vval.v_object->obj_class;
2088 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002089 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002090 char_u *fname = name_end + 1;
2091 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
2092 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00002093 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002094 len = fname_end - fname;
2095
Ernie Rael4d00b832023-09-11 19:54:42 +02002096 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002097
Bram Moolenaareb533502022-12-14 15:06:11 +00002098fail_after_eval:
2099 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002100 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002101}
2102
2103/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002104 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002105 * Returns -1, if the variable is not found.
2106 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002107 */
2108 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002109class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002110{
Ernie Rael4d00b832023-09-11 19:54:42 +02002111 int idx;
2112 class_member_lookup(cl, name, namelen, &idx);
2113 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002114}
2115
2116/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002117 * Returns a pointer to the class member variable "name" in the class "cl".
2118 * Returns NULL if the variable is not found.
2119 * The member variable index is set in "idx".
2120 */
2121 ocmember_T *
2122class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2123{
Ernie Rael4d00b832023-09-11 19:54:42 +02002124 ocmember_T *ret_m = NULL;
2125 int ret_idx = -1;
2126 for (int i = 0; i < cl->class_class_member_count; ++i)
2127 {
2128 ocmember_T *m = &cl->class_class_members[i];
2129 if (namelen)
2130 {
2131 if (STRNCMP(name, m->ocm_name, namelen) == 0
2132 && m->ocm_name[namelen] == NUL)
2133 {
2134 ret_m = m;
2135 ret_idx = i;
2136 break;
2137 }
2138 }
2139 else if (STRCMP(name, m->ocm_name) == 0)
2140 {
2141 ret_m = m;
2142 ret_idx = i;
2143 break;
2144 }
2145 }
2146 if (idx != NULL)
2147 *idx = ret_idx;
2148 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002149}
2150
2151/*
2152 * Returns the index of class method "name" in the class "cl".
2153 * Returns -1, if the method is not found.
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002154 */
2155 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002156class_method_idx(class_T *cl, char_u *name, size_t namelen)
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002157{
Ernie Rael4d00b832023-09-11 19:54:42 +02002158 int idx;
2159 class_method_lookup(cl, name, namelen, &idx);
2160 return idx;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002161}
2162
2163/*
2164 * Returns a pointer to the class method "name" in class "cl".
2165 * Returns NULL if the method is not found.
2166 * The method index is set in "idx".
2167 */
2168 ufunc_T *
2169class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2170{
Ernie Rael4d00b832023-09-11 19:54:42 +02002171 ufunc_T *ret_fp = NULL;
2172 int ret_idx = -1;
2173 for (int i = 0; i < cl->class_class_function_count; ++i)
2174 {
2175 ufunc_T *fp = cl->class_class_functions[i];
2176 char_u *ufname = (char_u *)fp->uf_name;
2177 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2178 {
2179 ret_fp = fp;
2180 ret_idx = i;
2181 break;
2182 }
2183 }
2184 if (idx != NULL)
2185 *idx = ret_idx;
2186 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002187}
2188
2189/*
2190 * Returns the index of object member variable "name" in the class "cl".
2191 * Returns -1, if the variable is not found.
2192 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
2193 */
2194 int
2195object_member_idx(class_T *cl, char_u *name, size_t namelen)
2196{
Ernie Rael4d00b832023-09-11 19:54:42 +02002197 int idx;
2198 object_member_lookup(cl, name, namelen, &idx);
2199 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002200}
2201
2202/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002203 * Returns a pointer to the object member variable "name" in the class "cl".
2204 * Returns NULL if the variable is not found.
2205 * The object member variable index is set in "idx".
2206 */
2207 ocmember_T *
2208object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2209{
Ernie Rael4d00b832023-09-11 19:54:42 +02002210 ocmember_T *ret_m = NULL;
2211 int ret_idx = -1;
2212 for (int i = 0; i < cl->class_obj_member_count; ++i)
2213 {
2214 ocmember_T *m = &cl->class_obj_members[i];
2215 if (namelen)
2216 {
2217 if (STRNCMP(name, m->ocm_name, namelen) == 0
2218 && m->ocm_name[namelen] == NUL)
2219 {
2220 ret_m = m;
2221 ret_idx = i;
2222 break;
2223 }
2224 }
2225 else if (STRCMP(name, m->ocm_name) == 0)
2226 {
2227 ret_m = m;
2228 ret_idx = i;
2229 break;
2230 }
2231 }
2232 if (idx != NULL)
2233 *idx = ret_idx;
2234 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002235}
2236
2237/*
2238 * Returns the index of object method "name" in the class "cl".
2239 * Returns -1, if the method is not found.
2240 */
2241 int
2242object_method_idx(class_T *cl, char_u *name, size_t namelen)
2243{
Ernie Rael4d00b832023-09-11 19:54:42 +02002244 int idx;
2245 object_method_lookup(cl, name, namelen, &idx);
2246 return idx;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002247}
2248
2249/*
2250 * Returns a pointer to the object method "name" in class "cl".
2251 * Returns NULL if the method is not found.
2252 * The object method index is set in "idx".
2253 */
2254 ufunc_T *
2255object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2256{
Ernie Rael4d00b832023-09-11 19:54:42 +02002257 ufunc_T *ret_fp = NULL;
2258 int ret_idx = -1;
2259 for (int i = 0; i < cl->class_obj_method_count; ++i)
2260 {
2261 ufunc_T *fp = cl->class_obj_methods[i];
2262 // Use a separate pointer to avoid that ASAN complains about
2263 // uf_name[] only being 4 characters.
2264 char_u *ufname = (char_u *)fp->uf_name;
2265 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2266 {
2267 ret_fp = fp;
2268 ret_idx = i;
2269 break;
2270 }
2271 }
2272 if (idx != NULL)
2273 *idx = ret_idx;
2274 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002275}
2276
2277/*
2278 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
2279 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
2280 * object member variable.
2281 *
2282 * Returns a pointer to the member variable structure if variable is found.
2283 * Otherwise returns NULL. The member variable index is set in "*idx".
2284 */
2285 ocmember_T *
2286member_lookup(
2287 class_T *cl,
2288 vartype_T v_type,
2289 char_u *name,
2290 size_t namelen,
2291 int *idx)
2292{
2293 if (v_type == VAR_CLASS)
2294 return class_member_lookup(cl, name, namelen, idx);
2295 else
2296 return object_member_lookup(cl, name, namelen, idx);
2297}
2298
2299/*
2300 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
2301 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
2302 *
2303 * Returns a pointer to the method structure if variable is found.
2304 * Otherwise returns NULL. The method variable index is set in "*idx".
2305 */
2306 ufunc_T *
2307method_lookup(
2308 class_T *cl,
2309 vartype_T v_type,
2310 char_u *name,
2311 size_t namelen,
2312 int *idx)
2313{
2314 if (v_type == VAR_CLASS)
2315 return class_method_lookup(cl, name, namelen, idx);
2316 else
2317 return object_method_lookup(cl, name, namelen, idx);
2318}
2319
2320/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00002321 * Return TRUE if current context "cctx_arg" is inside class "cl".
2322 * Return FALSE if not.
2323 */
2324 int
2325inside_class(cctx_T *cctx_arg, class_T *cl)
2326{
2327 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002328 if (cctx->ctx_ufunc != NULL
2329 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002330 return TRUE;
2331 return FALSE;
2332}
2333
2334/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002335 * Make a copy of an object.
2336 */
2337 void
2338copy_object(typval_T *from, typval_T *to)
2339{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002340 if (from->vval.v_object == NULL)
2341 to->vval.v_object = NULL;
2342 else
2343 {
2344 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002345 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002346 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002347}
2348
2349/*
2350 * Free an object.
2351 */
2352 static void
2353object_clear(object_T *obj)
2354{
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002355 // Avoid a recursive call, it can happen if "obj" has a circular reference.
2356 obj->obj_refcount = INT_MAX;
2357
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002358 class_T *cl = obj->obj_class;
2359
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002360 if (!cl)
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002361 return;
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002362
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002363 // the member values are just after the object structure
2364 typval_T *tv = (typval_T *)(obj + 1);
2365 for (int i = 0; i < cl->class_obj_member_count; ++i)
2366 clear_tv(tv + i);
2367
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002368 // Remove from the list headed by "first_object".
2369 object_cleared(obj);
2370
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002371 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002372 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002373}
2374
2375/*
2376 * Unreference an object.
2377 */
2378 void
2379object_unref(object_T *obj)
2380{
2381 if (obj != NULL && --obj->obj_refcount <= 0)
2382 object_clear(obj);
2383}
2384
2385/*
2386 * Make a copy of a class.
2387 */
2388 void
2389copy_class(typval_T *from, typval_T *to)
2390{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002391 if (from->vval.v_class == NULL)
2392 to->vval.v_class = NULL;
2393 else
2394 {
2395 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002396 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002397 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002398}
2399
2400/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002401 * Free the class "cl" and its contents.
2402 */
2403 static void
2404class_free(class_T *cl)
2405{
2406 // Freeing what the class contains may recursively come back here.
2407 // Clear "class_name" first, if it is NULL the class does not need to
2408 // be freed.
2409 VIM_CLEAR(cl->class_name);
2410
2411 class_unref(cl->class_extends);
2412
2413 for (int i = 0; i < cl->class_interface_count; ++i)
2414 {
2415 vim_free(((char_u **)cl->class_interfaces)[i]);
2416 if (cl->class_interfaces_cl[i] != NULL)
2417 class_unref(cl->class_interfaces_cl[i]);
2418 }
2419 vim_free(cl->class_interfaces);
2420 vim_free(cl->class_interfaces_cl);
2421
2422 itf2class_T *next;
2423 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2424 {
2425 next = i2c->i2c_next;
2426 vim_free(i2c);
2427 }
2428
2429 for (int i = 0; i < cl->class_class_member_count; ++i)
2430 {
2431 ocmember_T *m = &cl->class_class_members[i];
2432 vim_free(m->ocm_name);
2433 vim_free(m->ocm_init);
2434 if (cl->class_members_tv != NULL)
2435 clear_tv(&cl->class_members_tv[i]);
2436 }
2437 vim_free(cl->class_class_members);
2438 vim_free(cl->class_members_tv);
2439
2440 for (int i = 0; i < cl->class_obj_member_count; ++i)
2441 {
2442 ocmember_T *m = &cl->class_obj_members[i];
2443 vim_free(m->ocm_name);
2444 vim_free(m->ocm_init);
2445 }
2446 vim_free(cl->class_obj_members);
2447
2448 for (int i = 0; i < cl->class_class_function_count; ++i)
2449 {
2450 ufunc_T *uf = cl->class_class_functions[i];
2451 func_clear_free(uf, FALSE);
2452 }
2453 vim_free(cl->class_class_functions);
2454
2455 for (int i = 0; i < cl->class_obj_method_count; ++i)
2456 {
2457 ufunc_T *uf = cl->class_obj_methods[i];
2458 func_clear_free(uf, FALSE);
2459 }
2460 vim_free(cl->class_obj_methods);
2461
2462 clear_type_list(&cl->class_type_list);
2463
2464 class_cleared(cl);
2465
2466 vim_free(cl);
2467}
2468
2469/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002470 * Unreference a class. Free it when the reference count goes down to zero.
2471 */
2472 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002473class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002474{
Bram Moolenaard505d172022-12-18 21:42:55 +00002475 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002476 class_free(cl);
2477}
2478
2479/*
2480 * Go through the list of all classes and free items without "copyID".
2481 */
2482 int
2483class_free_nonref(int copyID)
2484{
2485 int did_free = FALSE;
2486
2487 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002488 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002489 next_nonref_class = cl->class_next_used;
2490 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002491 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002492 // Free the class and items it contains.
2493 class_free(cl);
2494 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002495 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002496 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002497
2498 next_nonref_class = NULL;
2499 return did_free;
2500}
2501
2502 int
2503set_ref_in_classes(int copyID)
2504{
2505 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
2506 set_ref_in_item_class(cl, copyID, NULL, NULL);
2507
2508 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002509}
2510
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002511static object_T *first_object = NULL;
2512
2513/*
2514 * Call this function when an object has been created. It will be added to the
2515 * list headed by "first_object".
2516 */
2517 void
2518object_created(object_T *obj)
2519{
2520 if (first_object != NULL)
2521 {
2522 obj->obj_next_used = first_object;
2523 first_object->obj_prev_used = obj;
2524 }
2525 first_object = obj;
2526}
2527
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002528static object_T *next_nonref_obj = NULL;
2529
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002530/*
2531 * Call this function when an object has been cleared and is about to be freed.
2532 * It is removed from the list headed by "first_object".
2533 */
2534 void
2535object_cleared(object_T *obj)
2536{
2537 if (obj->obj_next_used != NULL)
2538 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
2539 if (obj->obj_prev_used != NULL)
2540 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
2541 else if (first_object == obj)
2542 first_object = obj->obj_next_used;
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002543
2544 // update the next object to check if needed
2545 if (obj == next_nonref_obj)
2546 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002547}
2548
2549/*
2550 * Go through the list of all objects and free items without "copyID".
2551 */
2552 int
2553object_free_nonref(int copyID)
2554{
2555 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002556
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002557 for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002558 {
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002559 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002560 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
2561 {
2562 // Free the object and items it contains.
2563 object_clear(obj);
2564 did_free = TRUE;
2565 }
2566 }
2567
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002568 next_nonref_obj = NULL;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002569 return did_free;
2570}
2571
LemonBoyafe04662023-08-23 21:08:11 +02002572/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002573 * Echo a class or object method not found message.
2574 */
2575 void
2576method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
2577{
2578 char_u *method_name = vim_strnsave(name, len);
2579 if ((v_type == VAR_OBJECT)
2580 && (class_method_idx(cl, name, len) >= 0))
2581 {
2582 // If this is a class method, then give a different error
2583 if (*name == '_')
2584 semsg(_(e_cannot_access_private_method_str), method_name);
2585 else
2586 semsg(_(e_class_member_str_accessible_only_using_class_str),
2587 method_name, cl->class_name);
2588 }
2589 else if ((v_type == VAR_CLASS)
2590 && (object_method_idx(cl, name, len) >= 0))
2591 {
2592 // If this is an object method, then give a different error
2593 if (*name == '_')
2594 semsg(_(e_cannot_access_private_method_str), method_name);
2595 else
2596 semsg(_(e_object_member_str_accessible_only_using_object_str),
2597 method_name, cl->class_name);
2598 }
2599 else
2600 semsg(_(e_method_not_found_on_class_str_str), cl->class_name,
2601 method_name);
2602 vim_free(method_name);
2603}
2604
2605/*
2606 * Echo a class or object member not found message.
2607 */
2608 void
2609member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
2610{
2611 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
2612
2613 if (v_type == VAR_OBJECT)
2614 {
2615 if (class_member_idx(cl, name, len) >= 0)
2616 semsg(_(e_class_member_str_accessible_only_using_class_str),
2617 varname, cl->class_name);
2618 else
2619 semsg(_(e_member_not_found_on_object_str_str), cl->class_name,
2620 varname);
2621 }
2622 else
2623 {
2624 if (object_member_idx(cl, name, len) >= 0)
2625 semsg(_(e_object_member_str_accessible_only_using_object_str),
2626 varname, cl->class_name);
2627 else
2628 semsg(_(e_class_member_str_not_found_in_class_str),
2629 varname, cl->class_name);
2630 }
2631 vim_free(varname);
2632}
2633
2634/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002635 * Return TRUE when the class "cl", its base class or one of the implemented
2636 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02002637 */
2638 int
2639class_instance_of(class_T *cl, class_T *other_cl)
2640{
2641 if (cl == other_cl)
2642 return TRUE;
2643
2644 // Recursively check the base classes.
2645 for (; cl != NULL; cl = cl->class_extends)
2646 {
2647 if (cl == other_cl)
2648 return TRUE;
2649 // Check the implemented interfaces.
2650 for (int i = cl->class_interface_count - 1; i >= 0; --i)
2651 if (cl->class_interfaces_cl[i] == other_cl)
2652 return TRUE;
2653 }
2654
2655 return FALSE;
2656}
2657
2658/*
2659 * "instanceof(object, classinfo)" function
2660 */
2661 void
2662f_instanceof(typval_T *argvars, typval_T *rettv)
2663{
2664 typval_T *object_tv = &argvars[0];
2665 typval_T *classinfo_tv = &argvars[1];
2666 listitem_T *li;
2667
2668 rettv->vval.v_number = VVAL_FALSE;
2669
2670 if (check_for_object_arg(argvars, 0) == FAIL
2671 || check_for_class_or_list_arg(argvars, 1) == FAIL)
2672 return;
2673
2674 if (classinfo_tv->v_type == VAR_LIST)
2675 {
2676 FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
2677 {
2678 if (li->li_tv.v_type != VAR_CLASS)
2679 {
2680 emsg(_(e_class_required));
2681 return;
2682 }
2683
2684 if (class_instance_of(object_tv->vval.v_object->obj_class,
2685 li->li_tv.vval.v_class) == TRUE)
2686 {
2687 rettv->vval.v_number = VVAL_TRUE;
2688 return;
2689 }
2690 }
2691 }
2692 else if (classinfo_tv->v_type == VAR_CLASS)
2693 {
2694 rettv->vval.v_number = class_instance_of(object_tv->vval.v_object->obj_class,
2695 classinfo_tv->vval.v_class);
2696 }
2697}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002698
2699#endif // FEAT_EVAL