blob: f709c1f04db3e3f066c837f621344c056e3b5fe2 [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 Lakshmananfe7b20a2023-10-04 19:47:52 +020075 int *has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020076 garray_T *type_list,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020077 type_T **type_ret,
78 char_u **init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +000079{
80 *varname_end = to_name_end(varname, FALSE);
81 if (*varname == '_' && has_public)
82 {
RestorerZ7fe8f432023-09-24 23:21:24 +020083 semsg(_(e_public_variable_name_cannot_start_with_underscore_str), line);
Bram Moolenaard505d172022-12-18 21:42:55 +000084 return FAIL;
85 }
86
87 char_u *colon = skipwhite(*varname_end);
88 char_u *type_arg = colon;
89 type_T *type = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +020090 *has_type = FALSE;
Bram Moolenaard505d172022-12-18 21:42:55 +000091 if (*colon == ':')
92 {
93 if (VIM_ISWHITE(**varname_end))
94 {
95 semsg(_(e_no_white_space_allowed_before_colon_str), varname);
96 return FAIL;
97 }
98 if (!VIM_ISWHITE(colon[1]))
99 {
100 semsg(_(e_white_space_required_after_str_str), ":", varname);
101 return FAIL;
102 }
103 type_arg = skipwhite(colon + 1);
104 type = parse_type(&type_arg, type_list, TRUE);
105 if (type == NULL)
106 return FAIL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200107 *has_type = TRUE;
Bram Moolenaard505d172022-12-18 21:42:55 +0000108 }
109
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200110 char_u *init_arg = skipwhite(type_arg);
111 if (type == NULL && *init_arg != '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000112 {
113 emsg(_(e_type_or_initialization_required));
114 return FAIL;
115 }
116
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200117 if (init_expr == NULL && *init_arg == '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000118 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200119 emsg(_(e_cannot_initialize_variable_in_interface));
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200120 return FAIL;
121 }
122
123 if (*init_arg == '=')
124 {
125 evalarg_T evalarg;
126 char_u *expr_start, *expr_end;
127
128 if (!VIM_ISWHITE(init_arg[-1]) || !VIM_ISWHITE(init_arg[1]))
Bram Moolenaard505d172022-12-18 21:42:55 +0000129 {
130 semsg(_(e_white_space_required_before_and_after_str_at_str),
131 "=", type_arg);
132 return FAIL;
133 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200134 init_arg = skipwhite(init_arg + 1);
Bram Moolenaard505d172022-12-18 21:42:55 +0000135
Bram Moolenaard505d172022-12-18 21:42:55 +0000136 fill_evalarg_from_eap(&evalarg, eap, FALSE);
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200137 (void)skip_expr_concatenate(&init_arg, &expr_start, &expr_end, &evalarg);
Bram Moolenaard505d172022-12-18 21:42:55 +0000138
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +0200139 // No type specified for the member. Set it to "any" and the correct
140 // type will be set when the object is instantiated.
Bram Moolenaard505d172022-12-18 21:42:55 +0000141 if (type == NULL)
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200142 type = &t_any;
Bram Moolenaard505d172022-12-18 21:42:55 +0000143
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200144 *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
145 // Free the memory pointed by expr_start.
Bram Moolenaard505d172022-12-18 21:42:55 +0000146 clear_evalarg(&evalarg, NULL);
147 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200148 else if (!valid_declaration_type(type))
Bram Moolenaard505d172022-12-18 21:42:55 +0000149 return FAIL;
150
151 *type_ret = type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000152 return OK;
153}
154
155/*
156 * Add a member to an object or a class.
157 * Returns OK when successful, "init_expr" will be consumed then.
158 * Returns FAIL otherwise, caller might need to free "init_expr".
159 */
160 static int
161add_member(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200162 garray_T *gap,
163 char_u *varname,
164 char_u *varname_end,
165 int has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200166 int has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200167 type_T *type,
168 char_u *init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +0000169{
170 if (ga_grow(gap, 1) == FAIL)
171 return FAIL;
172 ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
173 m->ocm_name = vim_strnsave(varname, varname_end - varname);
=?UTF-8?q?Ola=20S=C3=B6der?=d8742472023-03-05 13:12:32 +0000174 m->ocm_access = has_public ? VIM_ACCESS_ALL
175 : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200176 m->ocm_has_type = has_type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000177 m->ocm_type = type;
178 if (init_expr != NULL)
179 m->ocm_init = init_expr;
180 ++gap->ga_len;
181 return OK;
182}
183
184/*
185 * Move the class or object members found while parsing a class into the class.
186 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000187 * "parent_members" points to the members in the parent class (if any)
188 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000189 * "members" will be set to the newly allocated array of members and
190 * "member_count" set to the number of members.
191 * Returns OK or FAIL.
192 */
193 static int
194add_members_to_class(
195 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000196 ocmember_T *parent_members,
197 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000198 ocmember_T **members,
199 int *member_count)
200{
Bram Moolenaar83677162023-01-08 19:54:10 +0000201 *member_count = parent_count + gap->ga_len;
202 *members = *member_count == 0 ? NULL
203 : ALLOC_MULT(ocmember_T, *member_count);
204 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000205 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000206 for (int i = 0; i < parent_count; ++i)
207 {
208 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000209 ocmember_T *m = *members + i;
210 *m = parent_members[i];
211 m->ocm_name = vim_strsave(m->ocm_name);
212 if (m->ocm_init != NULL)
213 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000214 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000215 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000216 // new members are moved
217 mch_memmove(*members + parent_count,
218 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000219 VIM_CLEAR(gap->ga_data);
220 return OK;
221}
222
223/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000224 * Convert a member index "idx" of interface "itf" to the member index of class
225 * "cl" implementing that interface.
226 */
227 int
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200228object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000229{
Ernie Rael18143d32023-09-04 22:30:41 +0200230 if (idx >= (is_method ? itf->class_obj_method_count
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200231 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000232 {
233 siemsg("index %d out of range for interface %s", idx, itf->class_name);
234 return 0;
235 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200236
237 // If "cl" is the interface or the class that is extended, then the method
238 // index can be used directly and there is no need to search for the method
239 // index in one of the child classes.
240 if (cl == itf)
241 return idx;
242
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200243 itf2class_T *i2c = NULL;
244 int searching = TRUE;
245 int method_offset = 0;
246
Ernie Raelcf138d42023-09-06 20:45:03 +0200247 for (class_T *super = cl; super != NULL && searching;
248 super = super->class_extends)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200249 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200250 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200251 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200252 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
253 {
254 searching = FALSE;
255 break;
256 }
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200257 }
258 if (searching && is_method)
259 // The parent class methods are stored after the current class
260 // methods.
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200261 method_offset += super->class_obj_method_count_child;
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200262 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000263 if (i2c == NULL)
264 {
265 siemsg("class %s not found on interface %s",
266 cl->class_name, itf->class_name);
267 return 0;
268 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +0200269
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200270 // A table follows the i2c for the class
271 int *table = (int *)(i2c + 1);
272 // "method_offset" is 0, if method is in the current class. If method
273 // is in a parent class, then it is non-zero.
274 return table[idx] + method_offset;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000275}
276
277/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200278 * Check whether a class named "extends_name" is present. If the class is
279 * valid, then "extends_clp" is set with the class pointer.
280 * Returns TRUE if the class name "extends_names" is a valid class.
281 */
282 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200283validate_extends_class(
284 char_u *extends_name,
285 class_T **extends_clp,
286 int is_class)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200287{
288 typval_T tv;
289 int success = FALSE;
290
291 tv.v_type = VAR_UNKNOWN;
292 if (eval_variable_import(extends_name, &tv) == FAIL)
293 {
294 semsg(_(e_class_name_not_found_str), extends_name);
295 return success;
296 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200297
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200298 if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
299 || (is_class
300 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
301 || (!is_class
302 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0))
303 // a interface cannot extend a class and a class cannot extend an
304 // interface.
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200305 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200306 else
307 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200308 class_T *extends_cl = tv.vval.v_class;
309 ++extends_cl->class_refcount;
310 *extends_clp = extends_cl;
311 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200312 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200313 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200314
315 return success;
316}
317
318/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200319 * Check method names in the parent class lineage to make sure the access is
320 * the same for overridden methods.
321 */
322 static int
323validate_extends_methods(
324 garray_T *objmethods_gap,
325 class_T *extends_cl)
326{
327 class_T *super = extends_cl;
328 int method_count = objmethods_gap->ga_len;
329 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
330
331 while (super != NULL)
332 {
333 int extends_method_count = super->class_obj_method_count_child;
334 if (extends_method_count == 0)
335 {
336 super = super->class_extends;
337 continue;
338 }
339
340 ufunc_T **extends_methods = super->class_obj_methods;
341
342 for (int i = 0; i < extends_method_count; i++)
343 {
344 char_u *pstr = extends_methods[i]->uf_name;
345 int extends_private = (*pstr == '_');
346 if (extends_private)
347 pstr++;
348
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200349 // When comparing the method names, ignore the access type (public
350 // and private methods are considered the same).
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200351 for (int j = 0; j < method_count; j++)
352 {
353 char_u *qstr = cl_fp[j]->uf_name;
354 int priv_method = (*qstr == '_');
355 if (priv_method)
356 qstr++;
357 if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
358 {
359 // Method access is different between the super class and
360 // the subclass
361 semsg(_(e_method_str_of_class_str_has_different_access),
362 cl_fp[j]->uf_name, super->class_name);
363 return FALSE;
364 }
365 }
366 }
367 super = super->class_extends;
368 }
369
370 return TRUE;
371}
372
373/*
374 * Check whether a object member variable in "objmembers_gap" is a duplicate of
375 * a member in any of the extended parent class lineage. Returns TRUE if there
376 * are no duplicates.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200377 */
378 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200379extends_check_dup_members(
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200380 garray_T *objmembers_gap,
381 class_T *extends_cl)
382{
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200383 int member_count = objmembers_gap->ga_len;
384 if (member_count == 0)
385 return TRUE;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200386
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200387 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
388
389 // Validate each member variable
390 for (int c_i = 0; c_i < member_count; c_i++)
391 {
392 class_T *p_cl = extends_cl;
393 ocmember_T *c_m = members + c_i;
394 char_u *pstr = (*c_m->ocm_name == '_')
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200395 ? c_m->ocm_name + 1 : c_m->ocm_name;
396
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200397 // Check in all the parent classes in the lineage
398 while (p_cl != NULL)
399 {
400 int p_member_count = p_cl->class_obj_member_count;
401 if (p_member_count == 0)
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200402 {
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200403 p_cl = p_cl->class_extends;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200404 continue;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200405 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200406 ocmember_T *p_members = p_cl->class_obj_members;
407
408 // Compare against all the members in the parent class
409 for (int p_i = 0; p_i < p_member_count; p_i++)
410 {
411 ocmember_T *p_m = p_members + p_i;
412 char_u *qstr = (*p_m->ocm_name == '_')
413 ? p_m->ocm_name + 1 : p_m->ocm_name;
414 if (STRCMP(pstr, qstr) == 0)
415 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200416 semsg(_(e_duplicate_variable_str), c_m->ocm_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200417 return FALSE;
418 }
419 }
420
421 p_cl = p_cl->class_extends;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200422 }
423 }
424
425 return TRUE;
426}
427
428/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200429 * Compare the variable type of interface variables in "objmembers_gap" against
430 * the variable in any of the extended super interface lineage. Used to
431 * compare the variable types when extending interfaces. Returns TRUE if the
432 * variable types are the same.
433 */
434 static int
435extends_check_intf_var_type(
436 garray_T *objmembers_gap,
437 class_T *extends_cl)
438{
439 int member_count = objmembers_gap->ga_len;
440 if (member_count == 0)
441 return TRUE;
442
443 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
444
445 // Validate each member variable
446 for (int c_i = 0; c_i < member_count; c_i++)
447 {
448 class_T *p_cl = extends_cl;
449 ocmember_T *c_m = members + c_i;
450 int var_found = FALSE;
451
452 // Check in all the parent classes in the lineage
453 while (p_cl != NULL && !var_found)
454 {
455 int p_member_count = p_cl->class_obj_member_count;
456 if (p_member_count == 0)
457 {
458 p_cl = p_cl->class_extends;
459 continue;
460 }
461 ocmember_T *p_members = p_cl->class_obj_members;
462
463 // Compare against all the members in the parent class
464 for (int p_i = 0; p_i < p_member_count; p_i++)
465 {
466 where_T where = WHERE_INIT;
467 ocmember_T *p_m = p_members + p_i;
468
469 if (STRCMP(p_m->ocm_name, c_m->ocm_name) != 0)
470 continue;
471
472 // Ensure the type is matching.
473 where.wt_func_name = (char *)c_m->ocm_name;
474 where.wt_kind = WT_MEMBER;
475
476 if (check_type(p_m->ocm_type, c_m->ocm_type, TRUE,
477 where) == FAIL)
478 return FALSE;
479
480 var_found = TRUE;
481 }
482
483 p_cl = p_cl->class_extends;
484 }
485 }
486
487 return TRUE;
488}
489
490/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200491 * When extending an abstract class, check whether all the abstract methods in
492 * the parent class are implemented. Returns TRUE if all the methods are
493 * implemented.
494 */
495 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200496validate_abstract_class_methods(
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200497 garray_T *classmethods_gap,
498 garray_T *objmethods_gap,
499 class_T *extends_cl)
500{
501 for (int loop = 1; loop <= 2; ++loop)
502 {
503 // loop == 1: check class methods
504 // loop == 2: check object methods
505 int extends_method_count = loop == 1
506 ? extends_cl->class_class_function_count
507 : extends_cl->class_obj_method_count;
508 if (extends_method_count == 0)
509 continue;
510
511 ufunc_T **extends_methods = loop == 1
512 ? extends_cl->class_class_functions
513 : extends_cl->class_obj_methods;
514
515 int method_count = loop == 1 ? classmethods_gap->ga_len
516 : objmethods_gap->ga_len;
517 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
518 ? classmethods_gap->ga_data
519 : objmethods_gap->ga_data);
520
521 for (int i = 0; i < extends_method_count; i++)
522 {
523 ufunc_T *uf = extends_methods[i];
Yegappan Lakshmanan1db15142023-09-19 20:34:05 +0200524 if (!IS_ABSTRACT_METHOD(uf))
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200525 continue;
526
527 int method_found = FALSE;
528
529 for (int j = 0; j < method_count; j++)
530 {
531 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
532 {
533 method_found = TRUE;
534 break;
535 }
536 }
537
538 if (!method_found)
539 {
540 semsg(_(e_abstract_method_str_not_found), uf->uf_name);
541 return FALSE;
542 }
543 }
544 }
545
546 return TRUE;
547}
548
549/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200550 * Returns TRUE if the interface variable "if_var" is present in the list of
551 * variables in "cl_mt" or in the parent lineage of one of the extended classes
552 * in "extends_cl". For a class variable, 'is_class_var' is TRUE.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200553 */
554 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200555intf_variable_present(
556 char_u *intf_class_name,
557 ocmember_T *if_var,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200558 ocmember_T *cl_mt,
559 int cl_member_count,
560 class_T *extends_cl)
561{
562 int variable_present = FALSE;
563
564 for (int cl_i = 0; cl_i < cl_member_count; ++cl_i)
565 {
566 ocmember_T *m = &cl_mt[cl_i];
567 where_T where = WHERE_INIT;
568
569 if (STRCMP(if_var->ocm_name, m->ocm_name) != 0)
570 continue;
571
572 // Ensure the access type is same
573 if (if_var->ocm_access != m->ocm_access)
574 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200575 semsg(_(e_variable_str_of_interface_str_has_different_access),
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200576 if_var->ocm_name, intf_class_name);
577 return FALSE;
578 }
579
580 // Ensure the type is matching.
581 if (m->ocm_type == &t_any)
582 {
583 // variable type is not specified. Use the variable type in the
584 // interface.
585 m->ocm_type = if_var->ocm_type;
586 }
587 else
588 {
589 where.wt_func_name = (char *)m->ocm_name;
590 where.wt_kind = WT_MEMBER;
591 if (check_type(if_var->ocm_type, m->ocm_type, TRUE,
592 where) == FAIL)
593 return FALSE;
594 }
595
596 variable_present = TRUE;
597 break;
598 }
599
600 if (!variable_present && extends_cl != NULL)
601 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200602 int ext_cl_count = extends_cl->class_obj_member_count;
603 ocmember_T *ext_cl_mt = extends_cl->class_obj_members;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200604 return intf_variable_present(intf_class_name, if_var,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200605 ext_cl_mt, ext_cl_count,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200606 extends_cl->class_extends);
607 }
608
609 return variable_present;
610}
611
612/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200613 * Check the variables of the interface class "ifcl" match object variables
614 * ("objmembers_gap") of a class.
615 * Returns TRUE if the object variables names are valid.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200616 */
617 static int
618validate_interface_variables(
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200619 char_u *intf_class_name,
620 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200621 garray_T *objmembers_gap,
622 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200623{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200624 int if_count = ifcl->class_obj_member_count;
625 if (if_count == 0)
626 return TRUE;
627
628 ocmember_T *if_ms = ifcl->class_obj_members;
629 ocmember_T *cl_ms = (ocmember_T *)(objmembers_gap->ga_data);
630 int cl_count = objmembers_gap->ga_len;
631 for (int if_i = 0; if_i < if_count; ++if_i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200632 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200633 if (!intf_variable_present(intf_class_name, &if_ms[if_i], cl_ms,
634 cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200635 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200636 semsg(_(e_variable_str_of_interface_str_not_implemented),
637 if_ms[if_i].ocm_name, intf_class_name);
638 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200639 }
640 }
641
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200642 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200643}
644
645/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200646 * Returns TRUE if the method signature of "if_method" and "cl_method" matches.
647 */
648 static int
649intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method)
650{
651 where_T where = WHERE_INIT;
652
653 // Ensure the type is matching.
654 where.wt_func_name = (char *)if_method->uf_name;
655 where.wt_kind = WT_METHOD;
656 if (check_type(if_method->uf_func_type, cl_method->uf_func_type, TRUE,
657 where) == FAIL)
658 return FALSE;
659
660 return TRUE;
661}
662
663/*
664 * Returns TRUE if the interface method "if_ufunc" is present in the list of
665 * methods in "cl_fp" or in the parent lineage of one of the extended classes
666 * in "extends_cl". For a class method, 'is_class_method' is TRUE.
667 */
668 static int
669intf_method_present(
670 ufunc_T *if_ufunc,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200671 ufunc_T **cl_fp,
672 int cl_count,
673 class_T *extends_cl)
674{
675 int method_present = FALSE;
676
677 for (int cl_i = 0; cl_i < cl_count; ++cl_i)
678 {
679 char_u *cl_name = cl_fp[cl_i]->uf_name;
680 if (STRCMP(if_ufunc->uf_name, cl_name) == 0)
681 {
682 // Ensure the type is matching.
683 if (!intf_method_type_matches(if_ufunc, cl_fp[cl_i]))
684 return FALSE;
685 method_present = TRUE;
686 break;
687 }
688 }
689
690 if (!method_present && extends_cl != NULL)
691 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200692 ufunc_T **ext_cl_fp = (ufunc_T **)(extends_cl->class_obj_methods);
693 int ext_cl_count = extends_cl->class_obj_method_count;
694 return intf_method_present(if_ufunc, ext_cl_fp, ext_cl_count,
695 extends_cl->class_extends);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200696 }
697
698 return method_present;
699}
700
701/*
702 * Validate that a new class implements all the class/instance methods in the
703 * interface "ifcl". The new class methods are in "classfunctions_gap" and the
704 * new object methods are in "objmemthods_gap". Also validates the method
705 * types.
706 * Returns TRUE if all the interface class/object methods are implemented in
707 * the new class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200708 */
709 static int
710validate_interface_methods(
711 char_u *intf_class_name,
712 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200713 garray_T *objmethods_gap,
714 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200715{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200716 int if_count = ifcl->class_obj_method_count;
717 if (if_count == 0)
718 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200719
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200720 ufunc_T **if_fp = ifcl->class_obj_methods;
721 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
722 int cl_count = objmethods_gap->ga_len;
723 for (int if_i = 0; if_i < if_count; ++if_i)
724 {
725 char_u *if_name = if_fp[if_i]->uf_name;
726
727 if (!intf_method_present(if_fp[if_i], cl_fp, cl_count, extends_cl))
728 {
729 semsg(_(e_method_str_of_interface_str_not_implemented),
730 if_name, intf_class_name);
731 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200732 }
733 }
734
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200735 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200736}
737
738/*
739 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200740 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200741 * object methods and object members in the new class are in
742 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
743 * "objmembers_gap" respectively.
744 */
745 static int
746validate_implements_classes(
747 garray_T *impl_gap,
748 class_T **intf_classes,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200749 garray_T *objmethods_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200750 garray_T *objmembers_gap,
751 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200752{
753 int success = TRUE;
754
755 for (int i = 0; i < impl_gap->ga_len && success; ++i)
756 {
757 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
758 typval_T tv;
759 tv.v_type = VAR_UNKNOWN;
760 if (eval_variable_import(impl, &tv) == FAIL)
761 {
762 semsg(_(e_interface_name_not_found_str), impl);
763 success = FALSE;
764 break;
765 }
766
767 if (tv.v_type != VAR_CLASS
768 || tv.vval.v_class == NULL
769 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
770 {
771 semsg(_(e_not_valid_interface_str), impl);
772 success = FALSE;
773 clear_tv(&tv);
774 break;
775 }
776
777 class_T *ifcl = tv.vval.v_class;
778 intf_classes[i] = ifcl;
779 ++ifcl->class_refcount;
780
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200781 // check the variables of the interface match the members of the class
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200782 success = validate_interface_variables(impl, ifcl, objmembers_gap,
783 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200784
785 // check the functions/methods of the interface match the
786 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200787 if (success)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200788 success = validate_interface_methods(impl, ifcl, objmethods_gap,
789 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200790 clear_tv(&tv);
791 }
792
793 return success;
794}
795
796/*
797 * Check no function argument name is used as a class member.
798 * (Object members are always accessed with "this." prefix, so no need
799 * to check them.)
800 */
801 static int
802check_func_arg_names(
803 garray_T *classfunctions_gap,
804 garray_T *objmethods_gap,
805 garray_T *classmembers_gap)
806{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200807 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200808 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200809 {
810 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
811
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200812 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200813 {
814 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
815
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200816 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200817 {
818 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
819 garray_T *mgap = classmembers_gap;
820
821 // Check all the class member names
822 for (int mi = 0; mi < mgap->ga_len; ++mi)
823 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200824 char_u *mname =
825 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200826 if (STRCMP(aname, mname) == 0)
827 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200828 if (uf->uf_script_ctx.sc_sid > 0)
829 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
830
831 semsg(_(e_argument_already_declared_in_class_str),
832 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200833
834 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200835 }
836 }
837 }
838 }
839 }
840
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200841 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200842}
843
844/*
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200845 * Returns TRUE if 'varname' is a reserved keyword name
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200846 */
847 static int
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200848is_reserved_varname(char_u *varname, char_u *varname_end)
849{
850 int reserved = FALSE;
851 char_u save_varname_end = *varname_end;
852 *varname_end = NUL;
853
854 reserved = check_reserved_name(varname, FALSE) == FAIL;
855
856 *varname_end = save_varname_end;
857
858 return reserved;
859}
860
861/*
862 * Returns TRUE if the variable "varname" is already defined either as a class
863 * variable or as an object variable.
864 */
865 static int
866is_duplicate_variable(
867 garray_T *class_members,
868 garray_T *obj_members,
869 char_u *varname,
870 char_u *varname_end)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200871{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200872 char_u *name = vim_strnsave(varname, varname_end - varname);
873 char_u *pstr = (*name == '_') ? name + 1 : name;
874 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200875
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200876 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200877 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200878 // loop == 1: class variables, loop == 2: object variables
879 garray_T *vgap = (loop == 1) ? class_members : obj_members;
880 for (int i = 0; i < vgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200881 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200882 ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i;
883 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1
884 : m->ocm_name;
885 if (STRCMP(pstr, qstr) == 0)
886 {
887 semsg(_(e_duplicate_variable_str), name);
888 dup = TRUE;
889 break;
890 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200891 }
892 }
893
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200894 vim_free(name);
895 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200896}
897
898/*
899 * Returns TRUE if the method "name" is already defined.
900 */
901 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200902is_duplicate_method(
903 garray_T *classmethods_gap,
904 garray_T *objmethods_gap,
905 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200906{
907 char_u *pstr = (*name == '_') ? name + 1 : name;
908
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200909 // loop 1: class methods, loop 2: object methods
910 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200911 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200912 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
913 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200914 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200915 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
916 char_u *qstr = *n == '_' ? n + 1 : n;
917 if (STRCMP(pstr, qstr) == 0)
918 {
919 semsg(_(e_duplicate_function_str), name);
920 return TRUE;
921 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200922 }
923 }
924
925 return FALSE;
926}
927
928/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200929 * Returns TRUE if the constructor is valid.
930 */
931 static int
932is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
933{
934 // Constructors are not allowed in abstract classes.
935 if (is_abstract)
936 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200937 emsg(_(e_cannot_define_new_method_in_abstract_class));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200938 return FALSE;
939 }
940 // A constructor is always static, no need to define it so.
941 if (has_static)
942 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200943 emsg(_(e_cannot_define_new_method_as_static));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200944 return FALSE;
945 }
946 // A return type should not be specified for the new()
947 // constructor method.
948 if (uf->uf_ret_type->tt_type != VAR_VOID)
949 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200950 emsg(_(e_cannot_use_a_return_type_with_new_method));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200951 return FALSE;
952 }
953 return TRUE;
954}
955
956/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200957 * Update the interface class lookup table for the member index on the
958 * interface to the member index in the class implementing the interface.
959 * And a lookup table for the object method index on the interface
960 * to the object method index in the class implementing the interface.
961 * This is also used for updating the lookup table for the extended class
962 * hierarchy.
963 */
964 static int
965update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200966 class_T *ifcl,
967 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200968 garray_T *objmethods,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200969 int pobj_method_offset)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200970{
971 if (ifcl == NULL)
972 return OK;
973
974 // Table for members.
975 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200976 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200977 if (if2cl == NULL)
978 return FAIL;
979 if2cl->i2c_next = ifcl->class_itf2class;
980 ifcl->class_itf2class = if2cl;
981 if2cl->i2c_class = cl;
982 if2cl->i2c_is_method = FALSE;
983
984 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
985 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
986 {
987 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200988 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200989 {
990 int *table = (int *)(if2cl + 1);
991 table[if_i] = cl_i;
992 break;
993 }
994 }
995
996 // Table for methods.
997 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200998 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200999 if (if2cl == NULL)
1000 return FAIL;
1001 if2cl->i2c_next = ifcl->class_itf2class;
1002 ifcl->class_itf2class = if2cl;
1003 if2cl->i2c_class = cl;
1004 if2cl->i2c_is_method = TRUE;
1005
1006 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
1007 {
1008 int done = FALSE;
1009 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
1010 {
1011 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001012 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001013 {
1014 int *table = (int *)(if2cl + 1);
1015 table[if_i] = cl_i;
1016 done = TRUE;
1017 break;
1018 }
1019 }
1020
1021 // extended class object method is not overridden by the child class.
1022 // Keep the method declared in one of the parent classes in the
1023 // lineage.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001024 if (!done)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001025 {
1026 // If "ifcl" is not the immediate parent of "cl", then search in
1027 // the intermediate parent classes.
1028 if (cl->class_extends != ifcl)
1029 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001030 class_T *parent = cl->class_extends;
1031 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001032
1033 while (!done && parent != NULL && parent != ifcl)
1034 {
1035
1036 for (int cl_i = 0;
1037 cl_i < parent->class_obj_method_count_child; ++cl_i)
1038 {
1039 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
1040 parent->class_obj_methods[cl_i]->uf_name)
1041 == 0)
1042 {
1043 int *table = (int *)(if2cl + 1);
1044 table[if_i] = method_offset + cl_i;
1045 done = TRUE;
1046 break;
1047 }
1048 }
1049 method_offset += parent->class_obj_method_count_child;
1050 parent = parent->class_extends;
1051 }
1052 }
1053
1054 if (!done)
1055 {
1056 int *table = (int *)(if2cl + 1);
1057 table[if_i] = pobj_method_offset + if_i;
1058 }
1059 }
1060 }
1061
1062 return OK;
1063}
1064
1065/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001066 * Update the member and object method lookup tables for a new class in the
1067 * interface class.
1068 * For each interface add a lookup table for the member index on the interface
1069 * to the member index in the new class. And a lookup table for the object
1070 * method index on the interface to the object method index in the new class.
1071 */
1072 static int
1073add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
1074{
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001075 // update the lookup table for all the implemented interfaces
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001076 for (int i = 0; i < cl->class_interface_count; ++i)
1077 {
1078 class_T *ifcl = cl->class_interfaces_cl[i];
1079
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001080 // update the lookup table for this interface and all its super
1081 // interfaces.
1082 while (ifcl != NULL)
1083 {
1084 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
1085 0) == FAIL)
1086 return FAIL;
1087 ifcl = ifcl->class_extends;
1088 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001089 }
1090
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001091 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001092 if (extends_cl != NULL)
1093 {
1094 class_T *pclass = extends_cl;
1095 int pobj_method_offset = objmethods_gap->ga_len;
1096
1097 // Update the entire lineage of extended classes.
1098 while (pclass != NULL)
1099 {
1100 if (update_member_method_lookup_table(pclass, cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001101 objmethods_gap, pobj_method_offset) == FAIL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001102 return FAIL;
1103
1104 pobj_method_offset += pclass->class_obj_method_count_child;
1105 pclass = pclass->class_extends;
1106 }
1107 }
1108
1109 return OK;
1110}
1111
1112/*
1113 * Add class members to a new class. Allocate a typval for each class member
1114 * and initialize it.
1115 */
1116 static void
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001117add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001118{
1119 // Allocate a typval for each class member and initialize it.
1120 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
1121 cl->class_class_member_count);
1122 if (cl->class_members_tv == NULL)
1123 return;
1124
1125 for (int i = 0; i < cl->class_class_member_count; ++i)
1126 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001127 ocmember_T *m = &cl->class_class_members[i];
1128 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001129 if (m->ocm_init != NULL)
1130 {
1131 typval_T *etv = eval_expr(m->ocm_init, eap);
1132 if (etv != NULL)
1133 {
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001134 if (m->ocm_type->tt_type == VAR_ANY
1135 && !m->ocm_has_type
1136 && etv->v_type != VAR_SPECIAL)
1137 // If the member variable type is not yet set, then use
1138 // the initialization expression type.
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001139 m->ocm_type = typval2type(etv, get_copyID(),
1140 type_list_gap,
1141 TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001142 *tv = *etv;
1143 vim_free(etv);
1144 }
1145 }
1146 else
1147 {
1148 // TODO: proper default value
1149 tv->v_type = m->ocm_type->tt_type;
1150 tv->vval.v_string = NULL;
1151 }
1152 }
1153}
1154
1155/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001156 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001157 */
1158 static void
1159add_default_constructor(
1160 class_T *cl,
1161 garray_T *classfunctions_gap,
1162 garray_T *type_list_gap)
1163{
1164 garray_T fga;
1165
1166 ga_init2(&fga, 1, 1000);
1167 ga_concat(&fga, (char_u *)"new(");
1168 for (int i = 0; i < cl->class_obj_member_count; ++i)
1169 {
1170 if (i > 0)
1171 ga_concat(&fga, (char_u *)", ");
1172 ga_concat(&fga, (char_u *)"this.");
1173 ocmember_T *m = cl->class_obj_members + i;
1174 ga_concat(&fga, (char_u *)m->ocm_name);
1175 ga_concat(&fga, (char_u *)" = v:none");
1176 }
1177 ga_concat(&fga, (char_u *)")\nenddef\n");
1178 ga_append(&fga, NUL);
1179
1180 exarg_T fea;
1181 CLEAR_FIELD(fea);
1182 fea.cmdidx = CMD_def;
1183 fea.cmd = fea.arg = fga.ga_data;
1184
1185 garray_T lines_to_free;
1186 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1187
h-eastb895b0f2023-09-24 15:46:31 +02001188 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
1189 cl->class_obj_members, cl->class_obj_member_count);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001190
1191 ga_clear_strings(&lines_to_free);
1192 vim_free(fga.ga_data);
1193
1194 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1195 {
1196 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001197 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001198 ++classfunctions_gap->ga_len;
1199
1200 nf->uf_flags |= FC_NEW;
1201 nf->uf_ret_type = get_type_ptr(type_list_gap);
1202 if (nf->uf_ret_type != NULL)
1203 {
1204 nf->uf_ret_type->tt_type = VAR_OBJECT;
1205 nf->uf_ret_type->tt_class = cl;
1206 nf->uf_ret_type->tt_argcount = 0;
1207 nf->uf_ret_type->tt_args = NULL;
1208 }
1209 }
1210}
1211
1212/*
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001213 * Add the class methods and object methods to the new class "cl".
1214 * When extending a class "extends_cl", add the instance methods from the
1215 * parent class also.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001216 */
1217 static int
1218add_classfuncs_objmethods(
1219 class_T *cl,
1220 class_T *extends_cl,
1221 garray_T *classfunctions_gap,
1222 garray_T *objmethods_gap)
1223{
1224 // loop 1: class functions, loop 2: object methods
1225 for (int loop = 1; loop <= 2; ++loop)
1226 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001227 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1228 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001229 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001230 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001231 : &cl->class_obj_methods;
1232
1233 int parent_count = 0;
1234 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001235 // Include object methods from the parent.
1236 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001237 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001238 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001239 : extends_cl->class_obj_method_count;
1240
1241 *fcount = parent_count + gap->ga_len;
1242 if (*fcount == 0)
1243 {
1244 *fup = NULL;
1245 continue;
1246 }
1247 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1248 if (*fup == NULL)
1249 return FAIL;
1250
1251 if (gap->ga_len != 0)
1252 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
1253 vim_free(gap->ga_data);
1254 if (loop == 1)
1255 cl->class_class_function_count_child = gap->ga_len;
1256 else
1257 cl->class_obj_method_count_child = gap->ga_len;
1258
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001259 if (loop == 2)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001260 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001261 // Copy instance methods from the parent.
1262
1263 for (int i = 0; i < parent_count; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001264 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001265 // Can't use the same parent function, because "uf_class" is
1266 // different and compilation will have a different result.
1267 // Put them after the functions in the current class, object
1268 // methods may be overruled, then "super.Method()" is used to
1269 // find a method from the parent.
1270 ufunc_T *pf = (extends_cl->class_obj_methods)[i];
1271 (*fup)[gap->ga_len + i] = copy_function(pf);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001272
1273 // If the child class overrides a function from the parent
1274 // the signature must be equal.
1275 char_u *pname = pf->uf_name;
1276 for (int ci = 0; ci < gap->ga_len; ++ci)
1277 {
1278 ufunc_T *cf = (*fup)[ci];
1279 char_u *cname = cf->uf_name;
1280 if (STRCMP(pname, cname) == 0)
1281 {
1282 where_T where = WHERE_INIT;
1283 where.wt_func_name = (char *)pname;
1284 where.wt_kind = WT_METHOD;
1285 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1286 TRUE, where);
1287 }
1288 }
1289 }
1290 }
1291
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001292 // Set the class pointer on all the functions and object methods.
1293 for (int i = 0; i < *fcount; ++i)
1294 {
1295 ufunc_T *fp = (*fup)[i];
1296 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001297 if (i < gap->ga_len)
1298 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001299 if (loop == 2)
1300 fp->uf_flags |= FC_OBJECT;
1301 }
1302 }
1303
1304 return OK;
1305}
1306
1307/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001308 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001309 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001310 */
1311 void
1312ex_class(exarg_T *eap)
1313{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001314 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1315 long start_lnum = SOURCING_LNUM;
1316 char_u *arg = eap->arg;
1317 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001318
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001319 if (is_abstract)
1320 {
1321 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1322 {
1323 semsg(_(e_invalid_argument_str), arg);
1324 return;
1325 }
1326 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001327 is_class = TRUE;
1328 }
1329
1330 if (!current_script_is_vim9()
1331 || (cmdmod.cmod_flags & CMOD_LEGACY)
1332 || !getline_equal(eap->getline, eap->cookie, getsourceline))
1333 {
1334 if (is_class)
1335 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1336 else
1337 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1338 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001339 }
1340
1341 if (!ASCII_ISUPPER(*arg))
1342 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001343 if (is_class)
1344 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1345 else
1346 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1347 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001348 return;
1349 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001350 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1351 if (!IS_WHITE_OR_NUL(*name_end))
1352 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001353 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001354 return;
1355 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001356 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001357
Bram Moolenaara86655a2023-01-12 17:06:27 +00001358 // "export class" gets used when creating the class, don't use "is_export"
1359 // for the items inside the class.
1360 int class_export = is_export;
1361 is_export = FALSE;
1362
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001363 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001364 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001365
Bram Moolenaar83677162023-01-08 19:54:10 +00001366 // Name for "extends BaseClass"
1367 char_u *extends = NULL;
1368
Bram Moolenaar94674f22023-01-06 18:42:20 +00001369 // Names for "implements SomeInterface"
1370 garray_T ga_impl;
1371 ga_init2(&ga_impl, sizeof(char_u *), 5);
1372
1373 arg = skipwhite(name_end);
1374 while (*arg != NUL && *arg != '#' && *arg != '\n')
1375 {
1376 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001377 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001378 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1379 {
1380 if (extends != NULL)
1381 {
1382 emsg(_(e_duplicate_extends));
1383 goto early_ret;
1384 }
1385 arg = skipwhite(arg + 7);
1386 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1387 if (!IS_WHITE_OR_NUL(*end))
1388 {
1389 semsg(_(e_white_space_required_after_name_str), arg);
1390 goto early_ret;
1391 }
1392 extends = vim_strnsave(arg, end - arg);
1393 if (extends == NULL)
1394 goto early_ret;
1395
1396 arg = skipwhite(end + 1);
1397 }
1398 else if (STRNCMP(arg, "implements", 10) == 0
1399 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001400 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001401 if (!is_class)
1402 {
1403 emsg(_(e_interface_cannot_use_implements));
1404 goto early_ret;
1405 }
1406
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001407 if (ga_impl.ga_len > 0)
1408 {
1409 emsg(_(e_duplicate_implements));
1410 goto early_ret;
1411 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001412 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001413
1414 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001415 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001416 char_u *impl_end = find_name_end(arg, NULL, NULL,
1417 FNE_CHECK_START);
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001418 if ((!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1419 || (*impl_end == ','
1420 && !IS_WHITE_OR_NUL(*(impl_end + 1))))
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001421 {
1422 semsg(_(e_white_space_required_after_name_str), arg);
1423 goto early_ret;
1424 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001425 if (impl_end - arg == 0)
1426 {
1427 emsg(_(e_missing_name_after_implements));
1428 goto early_ret;
1429 }
1430
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001431 char_u *iname = vim_strnsave(arg, impl_end - arg);
1432 if (iname == NULL)
1433 goto early_ret;
1434 for (int i = 0; i < ga_impl.ga_len; ++i)
1435 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1436 {
1437 semsg(_(e_duplicate_interface_after_implements_str),
1438 iname);
1439 vim_free(iname);
1440 goto early_ret;
1441 }
1442 if (ga_add_string(&ga_impl, iname) == FAIL)
1443 {
1444 vim_free(iname);
1445 goto early_ret;
1446 }
1447 if (*impl_end != ',')
1448 {
1449 arg = skipwhite(impl_end);
1450 break;
1451 }
1452 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001453 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001454 }
1455 else
1456 {
1457 semsg(_(e_trailing_characters_str), arg);
1458early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001459 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001460 ga_clear_strings(&ga_impl);
1461 return;
1462 }
1463 }
1464
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001465 garray_T type_list; // list of pointers to allocated types
1466 ga_init2(&type_list, sizeof(type_T *), 10);
1467
Bram Moolenaard505d172022-12-18 21:42:55 +00001468 // Growarray with class members declared in the class.
1469 garray_T classmembers;
1470 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1471
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001472 // Growarray with functions declared in the class.
1473 garray_T classfunctions;
1474 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001475
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001476 // Growarray with object members declared in the class.
1477 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001478 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001479
1480 // Growarray with object methods declared in the class.
1481 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001482 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001483
1484 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001485 * Go over the body of the class/interface until "endclass" or
1486 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001487 */
1488 char_u *theline = NULL;
1489 int success = FALSE;
1490 for (;;)
1491 {
1492 vim_free(theline);
1493 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
1494 if (theline == NULL)
1495 break;
1496 char_u *line = skipwhite(theline);
1497
Bram Moolenaar418b5472022-12-20 13:38:22 +00001498 // Skip empty and comment lines.
1499 if (*line == NUL)
1500 continue;
1501 if (*line == '#')
1502 {
1503 if (vim9_bad_comment(line))
1504 break;
1505 continue;
1506 }
1507
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001508 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001509 char *end_name = is_class ? "endclass" : "endinterface";
1510 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001511 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001512 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001513 semsg(_(e_command_cannot_be_shortened_str), line);
1514 else if (*p == '|' || !ends_excmd2(line, p))
1515 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001516 else
1517 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001518 break;
1519 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001520 char *wrong_name = is_class ? "endinterface" : "endclass";
1521 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1522 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001523 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001524 break;
1525 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001526
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001527 int has_public = FALSE;
1528 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001529 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001530 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001531 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001532 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001533 break;
1534 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001535 if (!is_class)
1536 {
Yegappan Lakshmananb90e3bc2023-09-28 23:06:48 +02001537 emsg(_(e_public_variable_not_supported_in_interface));
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001538 break;
1539 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001540 has_public = TRUE;
1541 p = skipwhite(line + 6);
1542
Bram Moolenaard505d172022-12-18 21:42:55 +00001543 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001544 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001545 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001546 break;
1547 }
1548 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001549
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001550 int abstract_method = FALSE;
1551 char_u *pa = p;
1552 if (checkforcmd(&p, "abstract", 3))
1553 {
1554 if (STRNCMP(pa, "abstract", 8) != 0)
1555 {
1556 semsg(_(e_command_cannot_be_shortened_str), pa);
1557 break;
1558 }
1559
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01001560 p = skipwhite(pa + 8);
1561 if (STRNCMP(p, "def", 3) != 0 && STRNCMP(p, "static", 6) != 0)
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001562 {
Christian Brabandt33c1da72023-11-02 22:45:12 +01001563 emsg(_(e_abstract_must_be_followed_by_def));
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01001564 break;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001565 }
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01001566
1567 if (!is_class)
1568 {
1569 // "abstract" not supported in an interface
1570 emsg(_(e_abstract_cannot_be_used_in_interface));
1571 break;
1572 }
1573
1574 if (!is_abstract)
1575 {
1576 semsg(_(e_abstract_method_in_concrete_class), pa);
1577 break;
1578 }
1579
1580 abstract_method = TRUE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001581 }
1582
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001583 int has_static = FALSE;
1584 char_u *ps = p;
1585 if (checkforcmd(&p, "static", 4))
1586 {
1587 if (STRNCMP(ps, "static", 6) != 0)
1588 {
1589 semsg(_(e_command_cannot_be_shortened_str), ps);
1590 break;
1591 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001592
1593 if (!is_class)
1594 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001595 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001596 break;
1597 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001598 has_static = TRUE;
1599 p = skipwhite(ps + 6);
1600 }
1601
Bram Moolenaard505d172022-12-18 21:42:55 +00001602 // object members (public, read access, private):
1603 // "this._varname"
1604 // "this.varname"
1605 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001606 if (STRNCMP(p, "this", 4) == 0)
1607 {
1608 if (p[4] != '.' || !eval_isnamec1(p[5]))
1609 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001610 semsg(_(e_invalid_object_variable_declaration_str), p);
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001611 break;
1612 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001613 if (has_static)
1614 {
1615 emsg(_(e_static_cannot_be_followed_by_this));
1616 break;
1617 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001618 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +00001619 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001620 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001621 char_u *init_expr = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001622 int has_type = FALSE;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001623
1624 if (!is_class && *varname == '_')
1625 {
1626 // private variables are not supported in an interface
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001627 semsg(_(e_private_variable_not_supported_in_interface),
1628 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001629 break;
1630 }
1631
Bram Moolenaard505d172022-12-18 21:42:55 +00001632 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001633 &varname_end, &has_type, &type_list, &type,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001634 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001635 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001636 if (is_reserved_varname(varname, varname_end))
1637 {
1638 vim_free(init_expr);
1639 break;
1640 }
1641 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1642 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001643 {
1644 vim_free(init_expr);
1645 break;
1646 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001647 if (add_member(&objmembers, varname, varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001648 has_public, has_type, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001649 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001650 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001651 break;
1652 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001653 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001654
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001655 // constructors:
1656 // def new()
1657 // enddef
1658 // def newOther()
1659 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001660 // object methods and class functions:
1661 // def SomeMethod()
1662 // enddef
1663 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001664 // enddef
1665 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001666 // def <Tval> someMethod()
1667 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001668 else if (checkforcmd(&p, "def", 3))
1669 {
1670 exarg_T ea;
1671 garray_T lines_to_free;
1672
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001673 if (has_public)
1674 {
1675 // "public" keyword is not supported when defining an object or
1676 // class method
1677 emsg(_(e_public_keyword_not_supported_for_method));
1678 break;
1679 }
1680
1681 if (*p == NUL)
1682 {
1683 // No method name following def
1684 semsg(_(e_not_valid_command_in_class_str), line);
1685 break;
1686 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001687
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001688 CLEAR_FIELD(ea);
1689 ea.cmd = line;
1690 ea.arg = p;
1691 ea.cmdidx = CMD_def;
1692 ea.getline = eap->getline;
1693 ea.cookie = eap->cookie;
1694
1695 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001696 int class_flags;
1697 if (is_class)
1698 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
1699 else
1700 class_flags = CF_INTERFACE;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001701 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02001702 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001703 ga_clear_strings(&lines_to_free);
1704
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001705 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001706 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001707 char_u *name = uf->uf_name;
1708 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001709
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001710 if (!is_class && *name == '_')
1711 {
1712 // private variables are not supported in an interface
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001713 semsg(_(e_private_method_not_supported_in_interface),
1714 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001715 func_clear_free(uf, FALSE);
1716 break;
1717 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001718 if (is_new && !is_valid_constructor(uf, is_abstract,
1719 has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001720 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001721 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001722 break;
1723 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001724
Bram Moolenaar58b40092023-01-11 15:59:05 +00001725 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001726 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001727 {
1728 success = FALSE;
1729 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001730 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001731 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001732
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001733 garray_T *fgap = has_static || is_new
1734 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001735 if (ga_grow(fgap, 1) == OK)
1736 {
1737 if (is_new)
1738 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001739
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001740 if (abstract_method)
1741 uf->uf_flags |= FC_ABSTRACT;
1742
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001743 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1744 ++fgap->ga_len;
1745 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001746 }
1747 }
1748
1749 // class members
1750 else if (has_static)
1751 {
1752 // class members (public, read access, private):
1753 // "static _varname"
1754 // "static varname"
1755 // "public static varname"
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001756 char_u *varname = p;
1757 char_u *varname_end = NULL;
1758 int has_type = FALSE;
1759 type_T *type = NULL;
1760 char_u *init_expr = NULL;
1761
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001762 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001763 &varname_end, &has_type, &type_list, &type,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001764 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001765 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001766 if (is_reserved_varname(varname, varname_end))
1767 {
1768 vim_free(init_expr);
1769 break;
1770 }
1771 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1772 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001773 {
1774 vim_free(init_expr);
1775 break;
1776 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001777 if (add_member(&classmembers, varname, varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001778 has_public, has_type, type, init_expr) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001779 {
1780 vim_free(init_expr);
1781 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001782 }
1783 }
1784
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001785 else
1786 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001787 if (is_class)
1788 semsg(_(e_not_valid_command_in_class_str), line);
1789 else
1790 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001791 break;
1792 }
1793 }
1794 vim_free(theline);
1795
Bram Moolenaar83677162023-01-08 19:54:10 +00001796 class_T *extends_cl = NULL; // class from "extends" argument
1797
1798 /*
1799 * Check a few things before defining the class.
1800 */
1801
1802 // Check the "extends" class is valid.
1803 if (success && extends != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001804 success = validate_extends_class(extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00001805 VIM_CLEAR(extends);
1806
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001807 // Check the new object methods to make sure their access (public or
1808 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001809 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001810 success = validate_extends_methods(&objmethods, extends_cl);
1811
1812 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001813 // variables in the extended class lineage. If an interface is extending
1814 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001815 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001816 {
1817 if (is_class)
1818 success = extends_check_dup_members(&objmembers, extends_cl);
1819 else
1820 success = extends_check_intf_var_type(&objmembers, extends_cl);
1821 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001822
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001823 // When extending an abstract class, make sure all the abstract methods in
1824 // the parent class are implemented. If the current class is an abstract
1825 // class, then there is no need for this check.
1826 if (success && !is_abstract && extends_cl != NULL
1827 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001828 success = validate_abstract_class_methods(&classfunctions,
1829 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001830
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001831 class_T **intf_classes = NULL;
1832
Bram Moolenaar83677162023-01-08 19:54:10 +00001833 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001834 if (success && ga_impl.ga_len > 0)
1835 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001836 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1837
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001838 success = validate_implements_classes(&ga_impl, intf_classes,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02001839 &objmethods, &objmembers, extends_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001840 }
1841
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001842 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001843 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001844 success = check_func_arg_names(&classfunctions, &objmethods,
1845 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001846
Bram Moolenaareb533502022-12-14 15:06:11 +00001847 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001848 if (success)
1849 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001850 // "endclass" encountered without failures: Create the class.
1851
Bram Moolenaareb533502022-12-14 15:06:11 +00001852 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001853 if (cl == NULL)
1854 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001855 if (!is_class)
1856 cl->class_flags = CLASS_INTERFACE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001857 else if (is_abstract)
1858 cl->class_flags = CLASS_ABSTRACT;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001859
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001860 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001861 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001862 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001863 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001864
Bram Moolenaard0200c82023-01-28 15:19:40 +00001865 if (extends_cl != NULL)
1866 {
1867 cl->class_extends = extends_cl;
1868 extends_cl->class_flags |= CLASS_EXTENDED;
1869 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001870
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001871 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00001872 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001873 NULL,
1874 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00001875 &cl->class_class_members,
1876 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001877 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001878 extends_cl == NULL ? NULL
1879 : extends_cl->class_obj_members,
1880 extends_cl == NULL ? 0
1881 : extends_cl->class_obj_member_count,
1882 &cl->class_obj_members,
1883 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001884 goto cleanup;
1885
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001886 if (ga_impl.ga_len > 0)
1887 {
1888 // Move the "implements" names into the class.
1889 cl->class_interface_count = ga_impl.ga_len;
1890 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1891 if (cl->class_interfaces == NULL)
1892 goto cleanup;
1893 for (int i = 0; i < ga_impl.ga_len; ++i)
1894 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1895 VIM_CLEAR(ga_impl.ga_data);
1896 ga_impl.ga_len = 0;
1897
Bram Moolenaard0200c82023-01-28 15:19:40 +00001898 cl->class_interfaces_cl = intf_classes;
1899 intf_classes = NULL;
1900 }
1901
1902 if (cl->class_interface_count > 0 || extends_cl != NULL)
1903 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001904 // Add a method and member lookup table to each of the interface
1905 // classes.
1906 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1907 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001908 }
1909
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001910 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001911 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001912 add_class_members(cl, eap, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001913
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001914 int have_new = FALSE;
1915 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001916 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001917 {
1918 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1919 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001920 {
1921 have_new = TRUE;
1922 break;
1923 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001924 }
1925
1926 if (have_new)
1927 // The return type of new() is an object of class "cl"
1928 class_func->uf_ret_type->tt_class = cl;
1929 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001930 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001931 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001932
Bram Moolenaar58b40092023-01-11 15:59:05 +00001933 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001934 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1935 &objmethods) == FAIL)
1936 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001937
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001938 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001939 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001940 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001941 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001942 cl->class_type_list = type_list;
1943
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001944 class_created(cl);
1945
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001946 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001947 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001948
1949 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001950 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02001951 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001952 typval_T tv;
1953 tv.v_type = VAR_CLASS;
1954 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001955 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001956 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001957 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001958 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001959 return;
1960 }
1961
1962cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00001963 if (cl != NULL)
1964 {
1965 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001966 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001967 if (cl->class_interfaces != NULL)
1968 {
1969 for (int i = 0; i < cl->class_interface_count; ++i)
1970 vim_free(cl->class_interfaces[i]);
1971 vim_free(cl->class_interfaces);
1972 }
1973 if (cl->class_interfaces_cl != NULL)
1974 {
1975 for (int i = 0; i < cl->class_interface_count; ++i)
1976 class_unref(cl->class_interfaces_cl[i]);
1977 vim_free(cl->class_interfaces_cl);
1978 }
Bram Moolenaareb533502022-12-14 15:06:11 +00001979 vim_free(cl->class_obj_members);
1980 vim_free(cl->class_obj_methods);
1981 vim_free(cl);
1982 }
1983
Bram Moolenaar83677162023-01-08 19:54:10 +00001984 vim_free(extends);
1985 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001986
1987 if (intf_classes != NULL)
1988 {
1989 for (int i = 0; i < ga_impl.ga_len; ++i)
1990 class_unref(intf_classes[i]);
1991 vim_free(intf_classes);
1992 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001993 ga_clear_strings(&ga_impl);
1994
Bram Moolenaard505d172022-12-18 21:42:55 +00001995 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001996 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001997 garray_T *gap = round == 1 ? &classmembers : &objmembers;
1998 if (gap->ga_len == 0 || gap->ga_data == NULL)
1999 continue;
2000
2001 for (int i = 0; i < gap->ga_len; ++i)
2002 {
2003 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2004 vim_free(m->ocm_name);
2005 vim_free(m->ocm_init);
2006 }
2007 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002008 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002009
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002010 for (int i = 0; i < objmethods.ga_len; ++i)
2011 {
2012 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2013 func_clear_free(uf, FALSE);
2014 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002015 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002016
2017 for (int i = 0; i < classfunctions.ga_len; ++i)
2018 {
2019 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2020 func_clear_free(uf, FALSE);
2021 }
2022 ga_clear(&classfunctions);
2023
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002024 clear_type_list(&type_list);
2025}
2026
2027/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002028 * Find member "name" in class "cl", set "member_idx" to the member index and
2029 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002030 * When "is_object" is TRUE, then look for object members. Otherwise look for
2031 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002032 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002033 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002034 */
2035 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002036oc_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002037 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002038 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002039 char_u *name,
2040 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002041 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002042{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002043 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002044 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002045
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002046 *member_idx = -1; // not found (yet)
2047
2048 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2049 member_idx);
2050 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002051 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002052 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2053 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002054 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002055 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002056
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002057 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002058}
2059
2060/*
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002061 * Given a class or object variable index, return the variable type
2062 */
2063 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002064oc_member_type_by_idx(
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002065 class_T *cl,
2066 int is_object,
2067 int member_idx)
2068{
2069 ocmember_T *m;
2070 int member_count;
2071
2072 if (is_object)
2073 {
2074 m = cl->class_obj_members;
2075 member_count = cl->class_obj_member_count;
2076 }
2077 else
2078 {
2079 m = cl->class_class_members;
2080 member_count = cl->class_class_member_count;
2081 }
2082
2083 if (member_idx >= member_count)
2084 return NULL;
2085
2086 return m[member_idx].ocm_type;
2087}
2088
2089/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002090 * Handle ":enum" up to ":endenum".
2091 */
2092 void
2093ex_enum(exarg_T *eap UNUSED)
2094{
2095 // TODO
2096}
2097
2098/*
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002099 * Type aliases (:type)
2100 */
2101
2102 void
2103typealias_free(typealias_T *ta)
2104{
2105 // ta->ta_type is freed in clear_type_list()
2106 vim_free(ta->ta_name);
2107 vim_free(ta);
2108}
2109
2110 void
2111typealias_unref(typealias_T *ta)
2112{
2113 if (ta != NULL && --ta->ta_refcount <= 0)
2114 typealias_free(ta);
2115}
2116
2117/*
2118 * Handle ":type". Create an alias for a type specification.
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002119 */
2120 void
2121ex_type(exarg_T *eap UNUSED)
2122{
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002123 char_u *arg = eap->arg;
2124
2125 if (!current_script_is_vim9()
2126 || (cmdmod.cmod_flags & CMOD_LEGACY)
2127 || !getline_equal(eap->getline, eap->cookie, getsourceline))
2128 {
2129 emsg(_(e_type_can_only_be_defined_in_vim9_script));
2130 return;
2131 }
2132
2133 if (*arg == NUL)
2134 {
2135 emsg(_(e_missing_typealias_name));
2136 return;
2137 }
2138
2139 if (!ASCII_ISUPPER(*arg))
2140 {
2141 semsg(_(e_type_name_must_start_with_uppercase_letter_str), arg);
2142 return;
2143 }
2144
2145 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
2146 if (!IS_WHITE_OR_NUL(*name_end))
2147 {
2148 semsg(_(e_white_space_required_after_name_str), arg);
2149 return;
2150 }
2151 char_u *name_start = arg;
2152
2153 arg = skipwhite(name_end);
2154 if (*arg != '=')
2155 {
2156 semsg(_(e_missing_equal_str), arg);
2157 return;
2158 }
2159 if (!IS_WHITE_OR_NUL(*(arg + 1)))
2160 {
2161 semsg(_(e_white_space_required_after_str_str), "=", arg);
2162 return;
2163 }
2164 arg++;
2165 arg = skipwhite(arg);
2166
2167 if (*arg == NUL)
2168 {
2169 emsg(_(e_missing_typealias_type));
2170 return;
2171 }
2172
2173 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
2174 type_T *type = parse_type(&arg, &si->sn_type_list, TRUE);
2175 if (type == NULL)
2176 return;
2177
2178 if (*arg != NUL)
2179 {
2180 // some text after the type
2181 semsg(_(e_trailing_characters_str), arg);
2182 return;
2183 }
2184
2185 int cc = *name_end;
2186 *name_end = NUL;
2187
2188 typval_T tv;
2189 tv.v_type = VAR_UNKNOWN;
2190 if (eval_variable_import(name_start, &tv) == OK)
2191 {
2192 if (tv.v_type == VAR_TYPEALIAS)
2193 semsg(_(e_typealias_already_exists_for_str), name_start);
2194 else
2195 semsg(_(e_redefining_script_item_str), name_start);
2196 clear_tv(&tv);
2197 goto done;
2198 }
2199
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02002200 // Create a script-local variable for the type alias.
2201 if (type->tt_type != VAR_OBJECT)
2202 {
2203 tv.v_type = VAR_TYPEALIAS;
2204 tv.v_lock = 0;
2205 tv.vval.v_typealias = ALLOC_CLEAR_ONE(typealias_T);
2206 ++tv.vval.v_typealias->ta_refcount;
2207 tv.vval.v_typealias->ta_name = vim_strsave(name_start);
2208 tv.vval.v_typealias->ta_type = type;
2209 }
2210 else
2211 {
2212 // When creating a type alias for a class, use the class type itself to
2213 // create the type alias variable. This is needed to use the type
2214 // alias to invoke class methods (e.g. new()) and use class variables.
2215 tv.v_type = VAR_CLASS;
2216 tv.v_lock = 0;
2217 tv.vval.v_class = type->tt_class;
2218 ++tv.vval.v_class->class_refcount;
2219 }
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002220 set_var_const(name_start, current_sctx.sc_sid, NULL, &tv, FALSE,
2221 ASSIGN_CONST | ASSIGN_FINAL, 0);
2222
2223done:
2224 *name_end = cc;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002225}
2226
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002227/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002228 * Returns OK if a member variable named "name" is present in the class "cl".
2229 * Otherwise returns FAIL. If found, the member variable typval is set in
2230 * "rettv". If "is_object" is TRUE, then the object member variable table is
2231 * searched. Otherwise the class member variable table is searched.
2232 */
2233 static int
2234get_member_tv(
2235 class_T *cl,
2236 int is_object,
2237 char_u *name,
2238 size_t namelen,
2239 typval_T *rettv)
2240{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002241 ocmember_T *m;
2242 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002243
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002244 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2245 &m_idx);
2246 if (m == NULL)
2247 return FAIL;
2248
2249 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002250 {
Ernie Raele6c9aa52023-10-06 19:55:52 +02002251 emsg_var_cl_define(e_cannot_access_private_variable_str,
2252 m->ocm_name, 0, cl);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002253 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002254 }
2255
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002256 if (is_object)
2257 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002258 // The object only contains a pointer to the class, the member values
2259 // array follows right after that.
2260 object_T *obj = rettv->vval.v_object;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002261 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2262 copy_tv(tv, rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002263 object_unref(obj);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002264 }
2265 else
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002266 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002267 copy_tv(&cl->class_members_tv[m_idx], rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002268 class_unref(cl);
2269 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002270
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002271 return OK;
2272}
2273
2274/*
2275 * Call an object or class method "name" in class "cl". The method return
2276 * value is returned in "rettv".
2277 */
2278 static int
2279call_oc_method(
2280 class_T *cl,
2281 char_u *name,
2282 size_t len,
2283 char_u *name_end,
2284 evalarg_T *evalarg,
2285 char_u **arg,
2286 typval_T *rettv)
2287{
2288 ufunc_T *fp;
2289 typval_T argvars[MAX_FUNC_ARGS + 1];
2290 int argcount = 0;
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002291 ocmember_T *ocm = NULL;
2292 int m_idx;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002293
2294 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
2295 if (fp == NULL)
2296 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002297 // could be an object or class funcref variable
2298 ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
2299 if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
2300 {
2301 method_not_found_msg(cl, rettv->v_type, name, len);
2302 return FAIL;
2303 }
2304
2305 if (rettv->v_type == VAR_OBJECT)
2306 {
2307 // funcref object variable
2308 object_T *obj = rettv->vval.v_object;
2309 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2310 copy_tv(tv, rettv);
2311 }
2312 else
2313 // funcref class variable
2314 copy_tv(&cl->class_members_tv[m_idx], rettv);
2315 *arg = name_end;
2316 return OK;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002317 }
2318
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002319 if (ocm == NULL && *fp->uf_name == '_')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002320 {
2321 // Cannot access a private method outside of a class
2322 semsg(_(e_cannot_access_private_method_str), fp->uf_name);
2323 return FAIL;
2324 }
2325
2326 char_u *argp = name_end;
2327 int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount);
2328 if (ret == FAIL)
2329 return FAIL;
2330
2331 funcexe_T funcexe;
2332 CLEAR_FIELD(funcexe);
2333 funcexe.fe_evaluate = TRUE;
2334 if (rettv->v_type == VAR_OBJECT)
2335 {
2336 funcexe.fe_object = rettv->vval.v_object;
2337 ++funcexe.fe_object->obj_refcount;
2338 }
2339
2340 // Clear the class or object after calling the function, in
2341 // case the refcount is one.
2342 typval_T tv_tofree = *rettv;
2343 rettv->v_type = VAR_UNKNOWN;
2344
2345 // Call the user function. Result goes into rettv;
2346 int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
2347 NULL);
2348
2349 // Clear the previous rettv and the arguments.
2350 clear_tv(&tv_tofree);
2351 for (int idx = 0; idx < argcount; ++idx)
2352 clear_tv(&argvars[idx]);
2353
2354 if (error != FCERR_NONE)
2355 {
2356 user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
2357 return FAIL;
2358 }
2359 *arg = argp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002360
2361 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002362}
2363
2364/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002365 * Evaluate what comes after a class:
2366 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002367 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002368 * - class constructor: SomeClass.new()
2369 * - object member: someObject.varname
2370 * - object method: someObject.SomeMethod()
2371 *
2372 * "*arg" points to the '.'.
2373 * "*arg" is advanced to after the member name or method call.
2374 *
2375 * Returns FAIL or OK.
2376 */
2377 int
2378class_object_index(
2379 char_u **arg,
2380 typval_T *rettv,
2381 evalarg_T *evalarg,
2382 int verbose UNUSED) // give error messages
2383{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002384 if (VIM_ISWHITE((*arg)[1]))
2385 {
2386 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
2387 return FAIL;
2388 }
2389
2390 ++*arg;
2391 char_u *name = *arg;
2392 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2393 if (name_end == name)
2394 return FAIL;
2395 size_t len = name_end - name;
2396
Ernie Raeld615a312023-10-05 20:28:16 +02002397 int did_emsg_save = did_emsg;
Bram Moolenaar552bdca2023-02-17 21:08:50 +00002398 class_T *cl;
2399 if (rettv->v_type == VAR_CLASS)
2400 cl = rettv->vval.v_class;
2401 else // VAR_OBJECT
2402 {
2403 if (rettv->vval.v_object == NULL)
2404 {
2405 emsg(_(e_using_null_object));
2406 return FAIL;
2407 }
2408 cl = rettv->vval.v_object->obj_class;
2409 }
2410
Bram Moolenaard13dd302023-03-11 20:56:35 +00002411 if (cl == NULL)
2412 {
2413 emsg(_(e_incomplete_type));
2414 return FAIL;
2415 }
2416
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002417 if (*name_end == '(')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002418 // Invoke the class or object method
2419 return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002420
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002421 else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002422 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002423 // Search in the object member variable table and the class member
2424 // variable table.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002425 int is_object = rettv->v_type == VAR_OBJECT;
2426 if (get_member_tv(cl, is_object, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002427 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002428 *arg = name_end;
2429 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002430 }
2431
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002432 // could be a class method or an object method
2433 int fidx;
2434 ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
2435 if (fp != NULL)
2436 {
2437 // Private methods are not accessible outside the class
2438 if (*name == '_')
2439 {
2440 semsg(_(e_cannot_access_private_method_str), fp->uf_name);
2441 return FAIL;
2442 }
2443
2444 partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
2445 if (pt == NULL)
2446 return FAIL;
2447
2448 pt->pt_refcount = 1;
2449 if (is_object)
2450 {
2451 pt->pt_obj = rettv->vval.v_object;
2452 ++pt->pt_obj->obj_refcount;
2453 }
2454 pt->pt_auto = TRUE;
2455 pt->pt_func = fp;
2456 func_ptr_ref(pt->pt_func);
2457 rettv->v_type = VAR_PARTIAL;
2458 rettv->vval.v_partial = pt;
2459 *arg = name_end;
2460 return OK;
2461 }
2462
Ernie Raeld615a312023-10-05 20:28:16 +02002463 if (did_emsg == did_emsg_save)
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +02002464 member_not_found_msg(cl, rettv->v_type, name, len);
Bram Moolenaard505d172022-12-18 21:42:55 +00002465 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002466
2467 return FAIL;
2468}
2469
2470/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002471 * If "arg" points to a class or object method, return it.
2472 * Otherwise return NULL.
2473 */
2474 ufunc_T *
2475find_class_func(char_u **arg)
2476{
2477 char_u *name = *arg;
2478 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2479 if (name_end == name || *name_end != '.')
2480 return NULL;
2481
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002482 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002483 size_t len = name_end - name;
2484 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002485 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00002486 if (eval_variable(name, (int)len,
2487 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002488 return NULL;
2489 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00002490 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002491
2492 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
2493 : tv.vval.v_object->obj_class;
2494 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002495 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002496 char_u *fname = name_end + 1;
2497 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
2498 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00002499 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002500 len = fname_end - fname;
2501
Ernie Rael4d00b832023-09-11 19:54:42 +02002502 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002503
Bram Moolenaareb533502022-12-14 15:06:11 +00002504fail_after_eval:
2505 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002506 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002507}
2508
2509/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002510 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002511 * Returns -1, if the variable is not found.
2512 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002513 */
2514 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002515class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002516{
Ernie Rael4d00b832023-09-11 19:54:42 +02002517 int idx;
2518 class_member_lookup(cl, name, namelen, &idx);
2519 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002520}
2521
2522/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002523 * Returns a pointer to the class member variable "name" in the class "cl".
2524 * Returns NULL if the variable is not found.
2525 * The member variable index is set in "idx".
2526 */
2527 ocmember_T *
2528class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2529{
Ernie Rael4d00b832023-09-11 19:54:42 +02002530 ocmember_T *ret_m = NULL;
2531 int ret_idx = -1;
2532 for (int i = 0; i < cl->class_class_member_count; ++i)
2533 {
2534 ocmember_T *m = &cl->class_class_members[i];
2535 if (namelen)
2536 {
2537 if (STRNCMP(name, m->ocm_name, namelen) == 0
2538 && m->ocm_name[namelen] == NUL)
2539 {
2540 ret_m = m;
2541 ret_idx = i;
2542 break;
2543 }
2544 }
2545 else if (STRCMP(name, m->ocm_name) == 0)
2546 {
2547 ret_m = m;
2548 ret_idx = i;
2549 break;
2550 }
2551 }
2552 if (idx != NULL)
2553 *idx = ret_idx;
2554 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002555}
2556
2557/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002558 * Returns a pointer to the class method "name" in class "cl".
2559 * Returns NULL if the method is not found.
2560 * The method index is set in "idx".
2561 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002562 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002563class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2564{
Ernie Rael4d00b832023-09-11 19:54:42 +02002565 ufunc_T *ret_fp = NULL;
2566 int ret_idx = -1;
2567 for (int i = 0; i < cl->class_class_function_count; ++i)
2568 {
2569 ufunc_T *fp = cl->class_class_functions[i];
2570 char_u *ufname = (char_u *)fp->uf_name;
2571 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2572 {
2573 ret_fp = fp;
2574 ret_idx = i;
2575 break;
2576 }
2577 }
2578 if (idx != NULL)
2579 *idx = ret_idx;
2580 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002581}
2582
2583/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002584 * Returns the index of class method "name" in the class "cl".
2585 * Returns -1, if the method is not found.
2586 */
2587 int
2588class_method_idx(class_T *cl, char_u *name, size_t namelen)
2589{
2590 int idx;
2591 class_method_lookup(cl, name, namelen, &idx);
2592 return idx;
2593}
2594
2595/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002596 * Returns the index of object member variable "name" in the class "cl".
2597 * Returns -1, if the variable is not found.
2598 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
2599 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002600 static int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002601object_member_idx(class_T *cl, char_u *name, size_t namelen)
2602{
Ernie Rael4d00b832023-09-11 19:54:42 +02002603 int idx;
2604 object_member_lookup(cl, name, namelen, &idx);
2605 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002606}
2607
2608/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002609 * Returns a pointer to the object member variable "name" in the class "cl".
2610 * Returns NULL if the variable is not found.
2611 * The object member variable index is set in "idx".
2612 */
2613 ocmember_T *
2614object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2615{
Ernie Rael4d00b832023-09-11 19:54:42 +02002616 ocmember_T *ret_m = NULL;
2617 int ret_idx = -1;
2618 for (int i = 0; i < cl->class_obj_member_count; ++i)
2619 {
2620 ocmember_T *m = &cl->class_obj_members[i];
2621 if (namelen)
2622 {
2623 if (STRNCMP(name, m->ocm_name, namelen) == 0
2624 && m->ocm_name[namelen] == NUL)
2625 {
2626 ret_m = m;
2627 ret_idx = i;
2628 break;
2629 }
2630 }
2631 else if (STRCMP(name, m->ocm_name) == 0)
2632 {
2633 ret_m = m;
2634 ret_idx = i;
2635 break;
2636 }
2637 }
2638 if (idx != NULL)
2639 *idx = ret_idx;
2640 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002641}
2642
2643/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002644 * Returns a pointer to the object method "name" in class "cl".
2645 * Returns NULL if the method is not found.
2646 * The object method index is set in "idx".
2647 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002648 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002649object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2650{
Ernie Rael4d00b832023-09-11 19:54:42 +02002651 ufunc_T *ret_fp = NULL;
2652 int ret_idx = -1;
2653 for (int i = 0; i < cl->class_obj_method_count; ++i)
2654 {
2655 ufunc_T *fp = cl->class_obj_methods[i];
2656 // Use a separate pointer to avoid that ASAN complains about
2657 // uf_name[] only being 4 characters.
2658 char_u *ufname = (char_u *)fp->uf_name;
2659 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2660 {
2661 ret_fp = fp;
2662 ret_idx = i;
2663 break;
2664 }
2665 }
2666 if (idx != NULL)
2667 *idx = ret_idx;
2668 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002669}
2670
2671/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002672 * Returns the index of object method "name" in the class "cl".
2673 * Returns -1, if the method is not found.
2674 */
2675 int
2676object_method_idx(class_T *cl, char_u *name, size_t namelen)
2677{
2678 int idx;
2679 object_method_lookup(cl, name, namelen, &idx);
2680 return idx;
2681}
2682
2683/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002684 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
2685 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
2686 * object member variable.
2687 *
2688 * Returns a pointer to the member variable structure if variable is found.
2689 * Otherwise returns NULL. The member variable index is set in "*idx".
2690 */
2691 ocmember_T *
2692member_lookup(
2693 class_T *cl,
2694 vartype_T v_type,
2695 char_u *name,
2696 size_t namelen,
2697 int *idx)
2698{
2699 if (v_type == VAR_CLASS)
2700 return class_member_lookup(cl, name, namelen, idx);
2701 else
2702 return object_member_lookup(cl, name, namelen, idx);
2703}
2704
2705/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02002706 * Find the class that defines the named member. Look up the hierarchy
2707 * starting at "cl".
2708 *
2709 * Return the class that defines the member "name", else NULL.
2710 * Fill in "p_m", if specified, for ocmember_T in found class.
2711 */
2712// NOTE: if useful for something could also indirectly return vartype and idx.
2713 static class_T *
2714class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m)
2715{
2716 class_T *cl_found = NULL;
2717 vartype_T vartype = VAR_UNKNOWN;
2718 ocmember_T *m_found = NULL;
2719
2720 len = len != 0 ? len : STRLEN(name);
2721
2722 // Loop assumes if member is not defined in "cl", then it is not
2723 // defined in any super class; the last class where it's found is the
2724 // class where it is defined. Once the vartype is found, the other
2725 // type is no longer checked.
2726 for (class_T *super = cl; super != NULL; super = super->class_extends)
2727 {
2728 class_T *cl_tmp = NULL;
2729 ocmember_T *m = NULL;
2730 if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT)
2731 {
2732 if ((m = object_member_lookup(super, name, len, NULL)) != NULL)
2733 {
2734 cl_tmp = super;
2735 vartype = VAR_OBJECT;
2736 }
2737 }
2738 if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS)
2739 {
2740 if (( m = class_member_lookup(super, name, len, NULL)) != NULL)
2741 {
2742 cl_tmp = super;
2743 vartype = VAR_OBJECT;
2744 }
2745 }
2746 if (cl_tmp == NULL)
2747 break; // member is not in this or any super class.
2748 cl_found = cl_tmp;
2749 m_found = m;
2750 }
2751 if (p_m != NULL)
2752 *p_m = m_found;
2753 return cl_found;
2754}
2755
2756/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002757 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
2758 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
2759 *
2760 * Returns a pointer to the method structure if variable is found.
2761 * Otherwise returns NULL. The method variable index is set in "*idx".
2762 */
2763 ufunc_T *
2764method_lookup(
2765 class_T *cl,
2766 vartype_T v_type,
2767 char_u *name,
2768 size_t namelen,
2769 int *idx)
2770{
2771 if (v_type == VAR_CLASS)
2772 return class_method_lookup(cl, name, namelen, idx);
2773 else
2774 return object_method_lookup(cl, name, namelen, idx);
2775}
2776
2777/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00002778 * Return TRUE if current context "cctx_arg" is inside class "cl".
2779 * Return FALSE if not.
2780 */
2781 int
2782inside_class(cctx_T *cctx_arg, class_T *cl)
2783{
2784 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002785 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002786 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002787 return TRUE;
2788 return FALSE;
2789}
2790
2791/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002792 * Make a copy of an object.
2793 */
2794 void
2795copy_object(typval_T *from, typval_T *to)
2796{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002797 if (from->vval.v_object == NULL)
2798 to->vval.v_object = NULL;
2799 else
2800 {
2801 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002802 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002803 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002804}
2805
2806/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002807 * Make a copy of a class.
2808 */
2809 void
2810copy_class(typval_T *from, typval_T *to)
2811{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002812 if (from->vval.v_class == NULL)
2813 to->vval.v_class = NULL;
2814 else
2815 {
2816 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002817 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002818 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002819}
2820
2821/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002822 * Free the class "cl" and its contents.
2823 */
2824 static void
2825class_free(class_T *cl)
2826{
2827 // Freeing what the class contains may recursively come back here.
2828 // Clear "class_name" first, if it is NULL the class does not need to
2829 // be freed.
2830 VIM_CLEAR(cl->class_name);
2831
2832 class_unref(cl->class_extends);
2833
2834 for (int i = 0; i < cl->class_interface_count; ++i)
2835 {
2836 vim_free(((char_u **)cl->class_interfaces)[i]);
2837 if (cl->class_interfaces_cl[i] != NULL)
2838 class_unref(cl->class_interfaces_cl[i]);
2839 }
2840 vim_free(cl->class_interfaces);
2841 vim_free(cl->class_interfaces_cl);
2842
2843 itf2class_T *next;
2844 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2845 {
2846 next = i2c->i2c_next;
2847 vim_free(i2c);
2848 }
2849
2850 for (int i = 0; i < cl->class_class_member_count; ++i)
2851 {
2852 ocmember_T *m = &cl->class_class_members[i];
2853 vim_free(m->ocm_name);
2854 vim_free(m->ocm_init);
2855 if (cl->class_members_tv != NULL)
2856 clear_tv(&cl->class_members_tv[i]);
2857 }
2858 vim_free(cl->class_class_members);
2859 vim_free(cl->class_members_tv);
2860
2861 for (int i = 0; i < cl->class_obj_member_count; ++i)
2862 {
2863 ocmember_T *m = &cl->class_obj_members[i];
2864 vim_free(m->ocm_name);
2865 vim_free(m->ocm_init);
2866 }
2867 vim_free(cl->class_obj_members);
2868
2869 for (int i = 0; i < cl->class_class_function_count; ++i)
2870 {
2871 ufunc_T *uf = cl->class_class_functions[i];
2872 func_clear_free(uf, FALSE);
2873 }
2874 vim_free(cl->class_class_functions);
2875
2876 for (int i = 0; i < cl->class_obj_method_count; ++i)
2877 {
2878 ufunc_T *uf = cl->class_obj_methods[i];
2879 func_clear_free(uf, FALSE);
2880 }
2881 vim_free(cl->class_obj_methods);
2882
2883 clear_type_list(&cl->class_type_list);
2884
2885 class_cleared(cl);
2886
2887 vim_free(cl);
2888}
2889
2890/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002891 * Unreference a class. Free it when the reference count goes down to zero.
2892 */
2893 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002894class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002895{
Bram Moolenaard505d172022-12-18 21:42:55 +00002896 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002897 class_free(cl);
2898}
2899
2900/*
2901 * Go through the list of all classes and free items without "copyID".
2902 */
2903 int
2904class_free_nonref(int copyID)
2905{
2906 int did_free = FALSE;
2907
2908 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002909 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002910 next_nonref_class = cl->class_next_used;
2911 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002912 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002913 // Free the class and items it contains.
2914 class_free(cl);
2915 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002916 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002917 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002918
2919 next_nonref_class = NULL;
2920 return did_free;
2921}
2922
2923 int
2924set_ref_in_classes(int copyID)
2925{
2926 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
2927 set_ref_in_item_class(cl, copyID, NULL, NULL);
2928
2929 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002930}
2931
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002932static object_T *first_object = NULL;
2933
2934/*
2935 * Call this function when an object has been created. It will be added to the
2936 * list headed by "first_object".
2937 */
2938 void
2939object_created(object_T *obj)
2940{
2941 if (first_object != NULL)
2942 {
2943 obj->obj_next_used = first_object;
2944 first_object->obj_prev_used = obj;
2945 }
2946 first_object = obj;
2947}
2948
2949/*
2950 * Call this function when an object has been cleared and is about to be freed.
2951 * It is removed from the list headed by "first_object".
2952 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002953 static void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002954object_cleared(object_T *obj)
2955{
2956 if (obj->obj_next_used != NULL)
2957 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
2958 if (obj->obj_prev_used != NULL)
2959 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
2960 else if (first_object == obj)
2961 first_object = obj->obj_next_used;
2962}
2963
2964/*
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002965 * Free the contents of an object ignoring the reference count.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002966 */
2967 static void
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002968object_free_contents(object_T *obj)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002969{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002970 class_T *cl = obj->obj_class;
2971
2972 if (!cl)
2973 return;
2974
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002975 // Avoid a recursive call, it can happen if "obj" has a circular reference.
2976 obj->obj_refcount = INT_MAX;
2977
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002978 // the member values are just after the object structure
2979 typval_T *tv = (typval_T *)(obj + 1);
2980 for (int i = 0; i < cl->class_obj_member_count; ++i)
2981 clear_tv(tv + i);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002982}
2983
2984 static void
2985object_free_object(object_T *obj)
2986{
2987 class_T *cl = obj->obj_class;
2988
2989 if (!cl)
2990 return;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002991
2992 // Remove from the list headed by "first_object".
2993 object_cleared(obj);
2994
2995 vim_free(obj);
2996 class_unref(cl);
2997}
2998
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002999 static void
3000object_free(object_T *obj)
3001{
3002 if (in_free_unref_items)
3003 return;
3004
3005 object_free_contents(obj);
3006 object_free_object(obj);
3007}
3008
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003009/*
3010 * Unreference an object.
3011 */
3012 void
3013object_unref(object_T *obj)
3014{
3015 if (obj != NULL && --obj->obj_refcount <= 0)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003016 object_free(obj);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003017}
3018
3019/*
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003020 * Go through the list of all objects and free items without "copyID".
3021 */
3022 int
3023object_free_nonref(int copyID)
3024{
3025 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003026
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003027 for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003028 {
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003029 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3030 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003031 // Free the object contents. Object itself will be freed later.
3032 object_free_contents(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003033 did_free = TRUE;
3034 }
3035 }
3036
3037 return did_free;
3038}
3039
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003040 void
3041object_free_items(int copyID)
3042{
3043 object_T *obj_next;
3044
3045 for (object_T *obj = first_object; obj != NULL; obj = obj_next)
3046 {
3047 obj_next = obj->obj_next_used;
3048 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3049 object_free_object(obj);
3050 }
3051}
3052
LemonBoyafe04662023-08-23 21:08:11 +02003053/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003054 * Output message which takes a variable name and the class that defines it.
3055 * "cl" is that class where the name was found. Search "cl"'s hierarchy to
3056 * find the defining class.
3057 */
3058 void
3059emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl)
3060{
3061 ocmember_T *m;
3062 class_T *cl_def = class_defining_member(cl, name, len, &m);
3063 if (cl_def != NULL)
3064 semsg(_(msg), m->ocm_name, cl_def->class_name);
3065 else
3066 emsg(_(e_internal_error_please_report_a_bug));
3067}
3068
3069/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003070 * Echo a class or object method not found message.
3071 */
3072 void
3073method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3074{
3075 char_u *method_name = vim_strnsave(name, len);
3076 if ((v_type == VAR_OBJECT)
3077 && (class_method_idx(cl, name, len) >= 0))
3078 {
3079 // If this is a class method, then give a different error
3080 if (*name == '_')
3081 semsg(_(e_cannot_access_private_method_str), method_name);
3082 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003083 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003084 method_name, cl->class_name);
3085 }
3086 else if ((v_type == VAR_CLASS)
3087 && (object_method_idx(cl, name, len) >= 0))
3088 {
3089 // If this is an object method, then give a different error
3090 if (*name == '_')
3091 semsg(_(e_cannot_access_private_method_str), method_name);
3092 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003093 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003094 method_name, cl->class_name);
3095 }
3096 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003097 semsg(_(e_method_not_found_on_class_str_str), method_name,
3098 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003099 vim_free(method_name);
3100}
3101
3102/*
3103 * Echo a class or object member not found message.
3104 */
3105 void
3106member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3107{
3108 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
3109
3110 if (v_type == VAR_OBJECT)
3111 {
3112 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003113 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003114 varname, cl->class_name);
3115 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003116 semsg(_(e_variable_not_found_on_object_str_str), varname,
3117 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003118 }
3119 else
3120 {
3121 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003122 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003123 varname, cl->class_name);
3124 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003125 semsg(_(e_class_variable_str_not_found_in_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003126 varname, cl->class_name);
3127 }
3128 vim_free(varname);
3129}
3130
3131/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02003132 * Return TRUE when the class "cl", its base class or one of the implemented
3133 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02003134 */
3135 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003136class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02003137{
3138 if (cl == other_cl)
3139 return TRUE;
3140
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003141 // Recursively check the base classes.
3142 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02003143 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003144 if (cl == other_cl)
3145 return TRUE;
3146 // Check the implemented interfaces and the super interfaces
3147 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003148 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003149 class_T *intf = cl->class_interfaces_cl[i];
3150 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003151 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003152 if (intf == other_cl)
3153 return TRUE;
3154 // check the super interfaces
3155 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003156 }
3157 }
LemonBoyafe04662023-08-23 21:08:11 +02003158 }
3159
3160 return FALSE;
3161}
3162
3163/*
3164 * "instanceof(object, classinfo)" function
3165 */
3166 void
3167f_instanceof(typval_T *argvars, typval_T *rettv)
3168{
3169 typval_T *object_tv = &argvars[0];
3170 typval_T *classinfo_tv = &argvars[1];
3171 listitem_T *li;
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02003172 class_T *c;
LemonBoyafe04662023-08-23 21:08:11 +02003173
3174 rettv->vval.v_number = VVAL_FALSE;
3175
3176 if (check_for_object_arg(argvars, 0) == FAIL
3177 || check_for_class_or_list_arg(argvars, 1) == FAIL)
3178 return;
3179
Ernie Rael3da696d2023-09-19 20:14:18 +02003180 if (object_tv->vval.v_object == NULL)
3181 return;
3182
LemonBoyafe04662023-08-23 21:08:11 +02003183 if (classinfo_tv->v_type == VAR_LIST)
3184 {
3185 FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
3186 {
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02003187 if (li->li_tv.v_type != VAR_CLASS && !tv_class_alias(&li->li_tv))
LemonBoyafe04662023-08-23 21:08:11 +02003188 {
3189 emsg(_(e_class_required));
3190 return;
3191 }
3192
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02003193 if (li->li_tv.v_type == VAR_TYPEALIAS)
3194 c = li->li_tv.vval.v_typealias->ta_type->tt_class;
3195 else
3196 c = li->li_tv.vval.v_class;
3197
3198 if (class_instance_of(object_tv->vval.v_object->obj_class, c)
3199 == TRUE)
LemonBoyafe04662023-08-23 21:08:11 +02003200 {
3201 rettv->vval.v_number = VVAL_TRUE;
3202 return;
3203 }
3204 }
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02003205
3206 return;
LemonBoyafe04662023-08-23 21:08:11 +02003207 }
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02003208
3209 if (classinfo_tv->v_type == VAR_TYPEALIAS)
3210 c = classinfo_tv->vval.v_typealias->ta_type->tt_class;
3211 else
3212 c = classinfo_tv->vval.v_class;
3213
3214 rettv->vval.v_number =
3215 class_instance_of(object_tv->vval.v_object->obj_class, c);
LemonBoyafe04662023-08-23 21:08:11 +02003216}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00003217
3218#endif // FEAT_EVAL