blob: a93cb29a0caaa0a1f590caf70761c8f67041849f [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 Lakshmanan92d9ee52023-09-17 17:03:19 +02001560 if (!is_class)
1561 // ignore "abstract" in an interface (as all the methods in an
1562 // interface are abstract.
1563 p = skipwhite(pa + 8);
1564 else
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001565 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001566 if (!is_abstract)
1567 {
1568 semsg(_(e_abstract_method_in_concrete_class), pa);
1569 break;
1570 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001571
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001572 abstract_method = TRUE;
1573 p = skipwhite(pa + 8);
1574 if (STRNCMP(p, "def", 3) != 0 && STRNCMP(p, "static", 6) != 0)
1575 {
1576 emsg(_(e_abstract_must_be_followed_by_def_or_static));
1577 break;
1578 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001579 }
1580 }
1581
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001582 int has_static = FALSE;
1583 char_u *ps = p;
1584 if (checkforcmd(&p, "static", 4))
1585 {
1586 if (STRNCMP(ps, "static", 6) != 0)
1587 {
1588 semsg(_(e_command_cannot_be_shortened_str), ps);
1589 break;
1590 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001591
1592 if (!is_class)
1593 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001594 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001595 break;
1596 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001597 has_static = TRUE;
1598 p = skipwhite(ps + 6);
1599 }
1600
Bram Moolenaard505d172022-12-18 21:42:55 +00001601 // object members (public, read access, private):
1602 // "this._varname"
1603 // "this.varname"
1604 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001605 if (STRNCMP(p, "this", 4) == 0)
1606 {
1607 if (p[4] != '.' || !eval_isnamec1(p[5]))
1608 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001609 semsg(_(e_invalid_object_variable_declaration_str), p);
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001610 break;
1611 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001612 if (has_static)
1613 {
1614 emsg(_(e_static_cannot_be_followed_by_this));
1615 break;
1616 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001617 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +00001618 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001619 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001620 char_u *init_expr = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001621 int has_type = FALSE;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001622
1623 if (!is_class && *varname == '_')
1624 {
1625 // private variables are not supported in an interface
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001626 semsg(_(e_private_variable_not_supported_in_interface),
1627 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001628 break;
1629 }
1630
Bram Moolenaard505d172022-12-18 21:42:55 +00001631 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001632 &varname_end, &has_type, &type_list, &type,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001633 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001634 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001635 if (is_reserved_varname(varname, varname_end))
1636 {
1637 vim_free(init_expr);
1638 break;
1639 }
1640 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1641 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001642 {
1643 vim_free(init_expr);
1644 break;
1645 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001646 if (add_member(&objmembers, varname, varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001647 has_public, has_type, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001648 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001649 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001650 break;
1651 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001652 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001653
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001654 // constructors:
1655 // def new()
1656 // enddef
1657 // def newOther()
1658 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001659 // object methods and class functions:
1660 // def SomeMethod()
1661 // enddef
1662 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001663 // enddef
1664 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001665 // def <Tval> someMethod()
1666 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001667 else if (checkforcmd(&p, "def", 3))
1668 {
1669 exarg_T ea;
1670 garray_T lines_to_free;
1671
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001672 if (has_public)
1673 {
1674 // "public" keyword is not supported when defining an object or
1675 // class method
1676 emsg(_(e_public_keyword_not_supported_for_method));
1677 break;
1678 }
1679
1680 if (*p == NUL)
1681 {
1682 // No method name following def
1683 semsg(_(e_not_valid_command_in_class_str), line);
1684 break;
1685 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001686
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001687 CLEAR_FIELD(ea);
1688 ea.cmd = line;
1689 ea.arg = p;
1690 ea.cmdidx = CMD_def;
1691 ea.getline = eap->getline;
1692 ea.cookie = eap->cookie;
1693
1694 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001695 int class_flags;
1696 if (is_class)
1697 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
1698 else
1699 class_flags = CF_INTERFACE;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001700 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02001701 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001702 ga_clear_strings(&lines_to_free);
1703
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001704 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001705 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001706 char_u *name = uf->uf_name;
1707 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001708
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001709 if (!is_class && *name == '_')
1710 {
1711 // private variables are not supported in an interface
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001712 semsg(_(e_private_method_not_supported_in_interface),
1713 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001714 func_clear_free(uf, FALSE);
1715 break;
1716 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001717 if (is_new && !is_valid_constructor(uf, is_abstract,
1718 has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001719 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001720 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001721 break;
1722 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001723
Bram Moolenaar58b40092023-01-11 15:59:05 +00001724 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001725 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001726 {
1727 success = FALSE;
1728 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001729 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001730 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001731
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001732 garray_T *fgap = has_static || is_new
1733 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001734 if (ga_grow(fgap, 1) == OK)
1735 {
1736 if (is_new)
1737 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001738
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001739 if (abstract_method)
1740 uf->uf_flags |= FC_ABSTRACT;
1741
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001742 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1743 ++fgap->ga_len;
1744 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001745 }
1746 }
1747
1748 // class members
1749 else if (has_static)
1750 {
1751 // class members (public, read access, private):
1752 // "static _varname"
1753 // "static varname"
1754 // "public static varname"
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001755 char_u *varname = p;
1756 char_u *varname_end = NULL;
1757 int has_type = FALSE;
1758 type_T *type = NULL;
1759 char_u *init_expr = NULL;
1760
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001761 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001762 &varname_end, &has_type, &type_list, &type,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001763 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001764 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001765 if (is_reserved_varname(varname, varname_end))
1766 {
1767 vim_free(init_expr);
1768 break;
1769 }
1770 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1771 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001772 {
1773 vim_free(init_expr);
1774 break;
1775 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001776 if (add_member(&classmembers, varname, varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001777 has_public, has_type, type, init_expr) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001778 {
1779 vim_free(init_expr);
1780 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001781 }
1782 }
1783
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001784 else
1785 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001786 if (is_class)
1787 semsg(_(e_not_valid_command_in_class_str), line);
1788 else
1789 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001790 break;
1791 }
1792 }
1793 vim_free(theline);
1794
Bram Moolenaar83677162023-01-08 19:54:10 +00001795 class_T *extends_cl = NULL; // class from "extends" argument
1796
1797 /*
1798 * Check a few things before defining the class.
1799 */
1800
1801 // Check the "extends" class is valid.
1802 if (success && extends != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001803 success = validate_extends_class(extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00001804 VIM_CLEAR(extends);
1805
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001806 // Check the new object methods to make sure their access (public or
1807 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001808 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001809 success = validate_extends_methods(&objmethods, extends_cl);
1810
1811 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001812 // variables in the extended class lineage. If an interface is extending
1813 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001814 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001815 {
1816 if (is_class)
1817 success = extends_check_dup_members(&objmembers, extends_cl);
1818 else
1819 success = extends_check_intf_var_type(&objmembers, extends_cl);
1820 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001821
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001822 // When extending an abstract class, make sure all the abstract methods in
1823 // the parent class are implemented. If the current class is an abstract
1824 // class, then there is no need for this check.
1825 if (success && !is_abstract && extends_cl != NULL
1826 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001827 success = validate_abstract_class_methods(&classfunctions,
1828 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001829
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001830 class_T **intf_classes = NULL;
1831
Bram Moolenaar83677162023-01-08 19:54:10 +00001832 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001833 if (success && ga_impl.ga_len > 0)
1834 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001835 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1836
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001837 success = validate_implements_classes(&ga_impl, intf_classes,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02001838 &objmethods, &objmembers, extends_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001839 }
1840
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001841 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001842 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001843 success = check_func_arg_names(&classfunctions, &objmethods,
1844 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001845
Bram Moolenaareb533502022-12-14 15:06:11 +00001846 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001847 if (success)
1848 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001849 // "endclass" encountered without failures: Create the class.
1850
Bram Moolenaareb533502022-12-14 15:06:11 +00001851 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001852 if (cl == NULL)
1853 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001854 if (!is_class)
1855 cl->class_flags = CLASS_INTERFACE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001856 else if (is_abstract)
1857 cl->class_flags = CLASS_ABSTRACT;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001858
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001859 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001860 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001861 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001862 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001863
Bram Moolenaard0200c82023-01-28 15:19:40 +00001864 if (extends_cl != NULL)
1865 {
1866 cl->class_extends = extends_cl;
1867 extends_cl->class_flags |= CLASS_EXTENDED;
1868 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001869
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001870 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00001871 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001872 NULL,
1873 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00001874 &cl->class_class_members,
1875 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001876 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001877 extends_cl == NULL ? NULL
1878 : extends_cl->class_obj_members,
1879 extends_cl == NULL ? 0
1880 : extends_cl->class_obj_member_count,
1881 &cl->class_obj_members,
1882 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001883 goto cleanup;
1884
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001885 if (ga_impl.ga_len > 0)
1886 {
1887 // Move the "implements" names into the class.
1888 cl->class_interface_count = ga_impl.ga_len;
1889 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1890 if (cl->class_interfaces == NULL)
1891 goto cleanup;
1892 for (int i = 0; i < ga_impl.ga_len; ++i)
1893 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1894 VIM_CLEAR(ga_impl.ga_data);
1895 ga_impl.ga_len = 0;
1896
Bram Moolenaard0200c82023-01-28 15:19:40 +00001897 cl->class_interfaces_cl = intf_classes;
1898 intf_classes = NULL;
1899 }
1900
1901 if (cl->class_interface_count > 0 || extends_cl != NULL)
1902 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001903 // Add a method and member lookup table to each of the interface
1904 // classes.
1905 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1906 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001907 }
1908
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001909 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001910 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001911 add_class_members(cl, eap, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001912
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001913 int have_new = FALSE;
1914 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001915 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001916 {
1917 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1918 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001919 {
1920 have_new = TRUE;
1921 break;
1922 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001923 }
1924
1925 if (have_new)
1926 // The return type of new() is an object of class "cl"
1927 class_func->uf_ret_type->tt_class = cl;
1928 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001929 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001930 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001931
Bram Moolenaar58b40092023-01-11 15:59:05 +00001932 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001933 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1934 &objmethods) == FAIL)
1935 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001936
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001937 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001938 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001939 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001940 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001941 cl->class_type_list = type_list;
1942
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001943 class_created(cl);
1944
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001945 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001946 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001947
1948 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001949 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02001950 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001951 typval_T tv;
1952 tv.v_type = VAR_CLASS;
1953 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001954 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001955 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001956 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001957 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001958 return;
1959 }
1960
1961cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00001962 if (cl != NULL)
1963 {
1964 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001965 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001966 if (cl->class_interfaces != NULL)
1967 {
1968 for (int i = 0; i < cl->class_interface_count; ++i)
1969 vim_free(cl->class_interfaces[i]);
1970 vim_free(cl->class_interfaces);
1971 }
1972 if (cl->class_interfaces_cl != NULL)
1973 {
1974 for (int i = 0; i < cl->class_interface_count; ++i)
1975 class_unref(cl->class_interfaces_cl[i]);
1976 vim_free(cl->class_interfaces_cl);
1977 }
Bram Moolenaareb533502022-12-14 15:06:11 +00001978 vim_free(cl->class_obj_members);
1979 vim_free(cl->class_obj_methods);
1980 vim_free(cl);
1981 }
1982
Bram Moolenaar83677162023-01-08 19:54:10 +00001983 vim_free(extends);
1984 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001985
1986 if (intf_classes != NULL)
1987 {
1988 for (int i = 0; i < ga_impl.ga_len; ++i)
1989 class_unref(intf_classes[i]);
1990 vim_free(intf_classes);
1991 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001992 ga_clear_strings(&ga_impl);
1993
Bram Moolenaard505d172022-12-18 21:42:55 +00001994 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001995 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001996 garray_T *gap = round == 1 ? &classmembers : &objmembers;
1997 if (gap->ga_len == 0 || gap->ga_data == NULL)
1998 continue;
1999
2000 for (int i = 0; i < gap->ga_len; ++i)
2001 {
2002 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2003 vim_free(m->ocm_name);
2004 vim_free(m->ocm_init);
2005 }
2006 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002007 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002008
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002009 for (int i = 0; i < objmethods.ga_len; ++i)
2010 {
2011 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2012 func_clear_free(uf, FALSE);
2013 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002014 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002015
2016 for (int i = 0; i < classfunctions.ga_len; ++i)
2017 {
2018 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2019 func_clear_free(uf, FALSE);
2020 }
2021 ga_clear(&classfunctions);
2022
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002023 clear_type_list(&type_list);
2024}
2025
2026/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002027 * Find member "name" in class "cl", set "member_idx" to the member index and
2028 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002029 * When "is_object" is TRUE, then look for object members. Otherwise look for
2030 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002031 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002032 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002033 */
2034 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002035oc_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002036 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002037 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002038 char_u *name,
2039 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002040 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002041{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002042 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002043 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002044
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002045 *member_idx = -1; // not found (yet)
2046
2047 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2048 member_idx);
2049 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002050 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002051 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2052 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002053 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002054 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002055
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002056 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002057}
2058
2059/*
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002060 * Given a class or object variable index, return the variable type
2061 */
2062 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002063oc_member_type_by_idx(
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002064 class_T *cl,
2065 int is_object,
2066 int member_idx)
2067{
2068 ocmember_T *m;
2069 int member_count;
2070
2071 if (is_object)
2072 {
2073 m = cl->class_obj_members;
2074 member_count = cl->class_obj_member_count;
2075 }
2076 else
2077 {
2078 m = cl->class_class_members;
2079 member_count = cl->class_class_member_count;
2080 }
2081
2082 if (member_idx >= member_count)
2083 return NULL;
2084
2085 return m[member_idx].ocm_type;
2086}
2087
2088/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002089 * Handle ":enum" up to ":endenum".
2090 */
2091 void
2092ex_enum(exarg_T *eap UNUSED)
2093{
2094 // TODO
2095}
2096
2097/*
2098 * Handle ":type".
2099 */
2100 void
2101ex_type(exarg_T *eap UNUSED)
2102{
2103 // TODO
2104}
2105
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002106/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002107 * Returns OK if a member variable named "name" is present in the class "cl".
2108 * Otherwise returns FAIL. If found, the member variable typval is set in
2109 * "rettv". If "is_object" is TRUE, then the object member variable table is
2110 * searched. Otherwise the class member variable table is searched.
2111 */
2112 static int
2113get_member_tv(
2114 class_T *cl,
2115 int is_object,
2116 char_u *name,
2117 size_t namelen,
2118 typval_T *rettv)
2119{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002120 ocmember_T *m;
2121 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002122
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002123 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2124 &m_idx);
2125 if (m == NULL)
2126 return FAIL;
2127
2128 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002129 {
Ernie Raele6c9aa52023-10-06 19:55:52 +02002130 emsg_var_cl_define(e_cannot_access_private_variable_str,
2131 m->ocm_name, 0, cl);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002132 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002133 }
2134
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002135 if (is_object)
2136 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002137 // The object only contains a pointer to the class, the member values
2138 // array follows right after that.
2139 object_T *obj = rettv->vval.v_object;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002140 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2141 copy_tv(tv, rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002142 object_unref(obj);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002143 }
2144 else
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002145 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002146 copy_tv(&cl->class_members_tv[m_idx], rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002147 class_unref(cl);
2148 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002149
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002150 return OK;
2151}
2152
2153/*
2154 * Call an object or class method "name" in class "cl". The method return
2155 * value is returned in "rettv".
2156 */
2157 static int
2158call_oc_method(
2159 class_T *cl,
2160 char_u *name,
2161 size_t len,
2162 char_u *name_end,
2163 evalarg_T *evalarg,
2164 char_u **arg,
2165 typval_T *rettv)
2166{
2167 ufunc_T *fp;
2168 typval_T argvars[MAX_FUNC_ARGS + 1];
2169 int argcount = 0;
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002170 ocmember_T *ocm = NULL;
2171 int m_idx;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002172
2173 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
2174 if (fp == NULL)
2175 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002176 // could be an object or class funcref variable
2177 ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
2178 if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
2179 {
2180 method_not_found_msg(cl, rettv->v_type, name, len);
2181 return FAIL;
2182 }
2183
2184 if (rettv->v_type == VAR_OBJECT)
2185 {
2186 // funcref object variable
2187 object_T *obj = rettv->vval.v_object;
2188 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2189 copy_tv(tv, rettv);
2190 }
2191 else
2192 // funcref class variable
2193 copy_tv(&cl->class_members_tv[m_idx], rettv);
2194 *arg = name_end;
2195 return OK;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002196 }
2197
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002198 if (ocm == NULL && *fp->uf_name == '_')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002199 {
2200 // Cannot access a private method outside of a class
2201 semsg(_(e_cannot_access_private_method_str), fp->uf_name);
2202 return FAIL;
2203 }
2204
2205 char_u *argp = name_end;
2206 int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount);
2207 if (ret == FAIL)
2208 return FAIL;
2209
2210 funcexe_T funcexe;
2211 CLEAR_FIELD(funcexe);
2212 funcexe.fe_evaluate = TRUE;
2213 if (rettv->v_type == VAR_OBJECT)
2214 {
2215 funcexe.fe_object = rettv->vval.v_object;
2216 ++funcexe.fe_object->obj_refcount;
2217 }
2218
2219 // Clear the class or object after calling the function, in
2220 // case the refcount is one.
2221 typval_T tv_tofree = *rettv;
2222 rettv->v_type = VAR_UNKNOWN;
2223
2224 // Call the user function. Result goes into rettv;
2225 int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
2226 NULL);
2227
2228 // Clear the previous rettv and the arguments.
2229 clear_tv(&tv_tofree);
2230 for (int idx = 0; idx < argcount; ++idx)
2231 clear_tv(&argvars[idx]);
2232
2233 if (error != FCERR_NONE)
2234 {
2235 user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
2236 return FAIL;
2237 }
2238 *arg = argp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002239
2240 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002241}
2242
2243/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002244 * Evaluate what comes after a class:
2245 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002246 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002247 * - class constructor: SomeClass.new()
2248 * - object member: someObject.varname
2249 * - object method: someObject.SomeMethod()
2250 *
2251 * "*arg" points to the '.'.
2252 * "*arg" is advanced to after the member name or method call.
2253 *
2254 * Returns FAIL or OK.
2255 */
2256 int
2257class_object_index(
2258 char_u **arg,
2259 typval_T *rettv,
2260 evalarg_T *evalarg,
2261 int verbose UNUSED) // give error messages
2262{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002263 if (VIM_ISWHITE((*arg)[1]))
2264 {
2265 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
2266 return FAIL;
2267 }
2268
2269 ++*arg;
2270 char_u *name = *arg;
2271 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2272 if (name_end == name)
2273 return FAIL;
2274 size_t len = name_end - name;
2275
Ernie Raeld615a312023-10-05 20:28:16 +02002276 int did_emsg_save = did_emsg;
Bram Moolenaar552bdca2023-02-17 21:08:50 +00002277 class_T *cl;
2278 if (rettv->v_type == VAR_CLASS)
2279 cl = rettv->vval.v_class;
2280 else // VAR_OBJECT
2281 {
2282 if (rettv->vval.v_object == NULL)
2283 {
2284 emsg(_(e_using_null_object));
2285 return FAIL;
2286 }
2287 cl = rettv->vval.v_object->obj_class;
2288 }
2289
Bram Moolenaard13dd302023-03-11 20:56:35 +00002290 if (cl == NULL)
2291 {
2292 emsg(_(e_incomplete_type));
2293 return FAIL;
2294 }
2295
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002296 if (*name_end == '(')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002297 // Invoke the class or object method
2298 return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002299
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002300 else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002301 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002302 // Search in the object member variable table and the class member
2303 // variable table.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002304 int is_object = rettv->v_type == VAR_OBJECT;
2305 if (get_member_tv(cl, is_object, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002306 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002307 *arg = name_end;
2308 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002309 }
2310
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002311 // could be a class method or an object method
2312 int fidx;
2313 ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
2314 if (fp != NULL)
2315 {
2316 // Private methods are not accessible outside the class
2317 if (*name == '_')
2318 {
2319 semsg(_(e_cannot_access_private_method_str), fp->uf_name);
2320 return FAIL;
2321 }
2322
2323 partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
2324 if (pt == NULL)
2325 return FAIL;
2326
2327 pt->pt_refcount = 1;
2328 if (is_object)
2329 {
2330 pt->pt_obj = rettv->vval.v_object;
2331 ++pt->pt_obj->obj_refcount;
2332 }
2333 pt->pt_auto = TRUE;
2334 pt->pt_func = fp;
2335 func_ptr_ref(pt->pt_func);
2336 rettv->v_type = VAR_PARTIAL;
2337 rettv->vval.v_partial = pt;
2338 *arg = name_end;
2339 return OK;
2340 }
2341
Ernie Raeld615a312023-10-05 20:28:16 +02002342 if (did_emsg == did_emsg_save)
Yegappan Lakshmanan00b55372023-10-19 17:18:28 +02002343 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2344 len);
Bram Moolenaard505d172022-12-18 21:42:55 +00002345 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002346
2347 return FAIL;
2348}
2349
2350/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002351 * If "arg" points to a class or object method, return it.
2352 * Otherwise return NULL.
2353 */
2354 ufunc_T *
2355find_class_func(char_u **arg)
2356{
2357 char_u *name = *arg;
2358 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2359 if (name_end == name || *name_end != '.')
2360 return NULL;
2361
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002362 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002363 size_t len = name_end - name;
2364 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002365 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00002366 if (eval_variable(name, (int)len,
2367 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002368 return NULL;
2369 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00002370 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002371
2372 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
2373 : tv.vval.v_object->obj_class;
2374 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002375 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002376 char_u *fname = name_end + 1;
2377 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
2378 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00002379 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002380 len = fname_end - fname;
2381
Ernie Rael4d00b832023-09-11 19:54:42 +02002382 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002383
Bram Moolenaareb533502022-12-14 15:06:11 +00002384fail_after_eval:
2385 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002386 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002387}
2388
2389/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002390 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002391 * Returns -1, if the variable is not found.
2392 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002393 */
2394 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002395class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002396{
Ernie Rael4d00b832023-09-11 19:54:42 +02002397 int idx;
2398 class_member_lookup(cl, name, namelen, &idx);
2399 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002400}
2401
2402/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002403 * Returns a pointer to the class member variable "name" in the class "cl".
2404 * Returns NULL if the variable is not found.
2405 * The member variable index is set in "idx".
2406 */
2407 ocmember_T *
2408class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2409{
Ernie Rael4d00b832023-09-11 19:54:42 +02002410 ocmember_T *ret_m = NULL;
2411 int ret_idx = -1;
2412 for (int i = 0; i < cl->class_class_member_count; ++i)
2413 {
2414 ocmember_T *m = &cl->class_class_members[i];
2415 if (namelen)
2416 {
2417 if (STRNCMP(name, m->ocm_name, namelen) == 0
2418 && m->ocm_name[namelen] == NUL)
2419 {
2420 ret_m = m;
2421 ret_idx = i;
2422 break;
2423 }
2424 }
2425 else if (STRCMP(name, m->ocm_name) == 0)
2426 {
2427 ret_m = m;
2428 ret_idx = i;
2429 break;
2430 }
2431 }
2432 if (idx != NULL)
2433 *idx = ret_idx;
2434 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002435}
2436
2437/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002438 * Returns a pointer to the class method "name" in class "cl".
2439 * Returns NULL if the method is not found.
2440 * The method index is set in "idx".
2441 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002442 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002443class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2444{
Ernie Rael4d00b832023-09-11 19:54:42 +02002445 ufunc_T *ret_fp = NULL;
2446 int ret_idx = -1;
2447 for (int i = 0; i < cl->class_class_function_count; ++i)
2448 {
2449 ufunc_T *fp = cl->class_class_functions[i];
2450 char_u *ufname = (char_u *)fp->uf_name;
2451 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2452 {
2453 ret_fp = fp;
2454 ret_idx = i;
2455 break;
2456 }
2457 }
2458 if (idx != NULL)
2459 *idx = ret_idx;
2460 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002461}
2462
2463/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002464 * Returns the index of class method "name" in the class "cl".
2465 * Returns -1, if the method is not found.
2466 */
2467 int
2468class_method_idx(class_T *cl, char_u *name, size_t namelen)
2469{
2470 int idx;
2471 class_method_lookup(cl, name, namelen, &idx);
2472 return idx;
2473}
2474
2475/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002476 * Returns the index of object member variable "name" in the class "cl".
2477 * Returns -1, if the variable is not found.
2478 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
2479 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002480 static int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002481object_member_idx(class_T *cl, char_u *name, size_t namelen)
2482{
Ernie Rael4d00b832023-09-11 19:54:42 +02002483 int idx;
2484 object_member_lookup(cl, name, namelen, &idx);
2485 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002486}
2487
2488/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002489 * Returns a pointer to the object member variable "name" in the class "cl".
2490 * Returns NULL if the variable is not found.
2491 * The object member variable index is set in "idx".
2492 */
2493 ocmember_T *
2494object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2495{
Ernie Rael4d00b832023-09-11 19:54:42 +02002496 ocmember_T *ret_m = NULL;
2497 int ret_idx = -1;
2498 for (int i = 0; i < cl->class_obj_member_count; ++i)
2499 {
2500 ocmember_T *m = &cl->class_obj_members[i];
2501 if (namelen)
2502 {
2503 if (STRNCMP(name, m->ocm_name, namelen) == 0
2504 && m->ocm_name[namelen] == NUL)
2505 {
2506 ret_m = m;
2507 ret_idx = i;
2508 break;
2509 }
2510 }
2511 else if (STRCMP(name, m->ocm_name) == 0)
2512 {
2513 ret_m = m;
2514 ret_idx = i;
2515 break;
2516 }
2517 }
2518 if (idx != NULL)
2519 *idx = ret_idx;
2520 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002521}
2522
2523/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002524 * Returns a pointer to the object method "name" in class "cl".
2525 * Returns NULL if the method is not found.
2526 * The object method index is set in "idx".
2527 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002528 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002529object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2530{
Ernie Rael4d00b832023-09-11 19:54:42 +02002531 ufunc_T *ret_fp = NULL;
2532 int ret_idx = -1;
2533 for (int i = 0; i < cl->class_obj_method_count; ++i)
2534 {
2535 ufunc_T *fp = cl->class_obj_methods[i];
2536 // Use a separate pointer to avoid that ASAN complains about
2537 // uf_name[] only being 4 characters.
2538 char_u *ufname = (char_u *)fp->uf_name;
2539 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2540 {
2541 ret_fp = fp;
2542 ret_idx = i;
2543 break;
2544 }
2545 }
2546 if (idx != NULL)
2547 *idx = ret_idx;
2548 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002549}
2550
2551/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002552 * Returns the index of object method "name" in the class "cl".
2553 * Returns -1, if the method is not found.
2554 */
2555 int
2556object_method_idx(class_T *cl, char_u *name, size_t namelen)
2557{
2558 int idx;
2559 object_method_lookup(cl, name, namelen, &idx);
2560 return idx;
2561}
2562
2563/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002564 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
2565 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
2566 * object member variable.
2567 *
2568 * Returns a pointer to the member variable structure if variable is found.
2569 * Otherwise returns NULL. The member variable index is set in "*idx".
2570 */
2571 ocmember_T *
2572member_lookup(
2573 class_T *cl,
2574 vartype_T v_type,
2575 char_u *name,
2576 size_t namelen,
2577 int *idx)
2578{
2579 if (v_type == VAR_CLASS)
2580 return class_member_lookup(cl, name, namelen, idx);
2581 else
2582 return object_member_lookup(cl, name, namelen, idx);
2583}
2584
2585/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02002586 * Find the class that defines the named member. Look up the hierarchy
2587 * starting at "cl".
2588 *
2589 * Return the class that defines the member "name", else NULL.
2590 * Fill in "p_m", if specified, for ocmember_T in found class.
2591 */
2592// NOTE: if useful for something could also indirectly return vartype and idx.
2593 static class_T *
2594class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m)
2595{
2596 class_T *cl_found = NULL;
2597 vartype_T vartype = VAR_UNKNOWN;
2598 ocmember_T *m_found = NULL;
2599
2600 len = len != 0 ? len : STRLEN(name);
2601
2602 // Loop assumes if member is not defined in "cl", then it is not
2603 // defined in any super class; the last class where it's found is the
2604 // class where it is defined. Once the vartype is found, the other
2605 // type is no longer checked.
2606 for (class_T *super = cl; super != NULL; super = super->class_extends)
2607 {
2608 class_T *cl_tmp = NULL;
2609 ocmember_T *m = NULL;
2610 if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT)
2611 {
2612 if ((m = object_member_lookup(super, name, len, NULL)) != NULL)
2613 {
2614 cl_tmp = super;
2615 vartype = VAR_OBJECT;
2616 }
2617 }
2618 if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS)
2619 {
2620 if (( m = class_member_lookup(super, name, len, NULL)) != NULL)
2621 {
2622 cl_tmp = super;
2623 vartype = VAR_OBJECT;
2624 }
2625 }
2626 if (cl_tmp == NULL)
2627 break; // member is not in this or any super class.
2628 cl_found = cl_tmp;
2629 m_found = m;
2630 }
2631 if (p_m != NULL)
2632 *p_m = m_found;
2633 return cl_found;
2634}
2635
2636/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002637 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
2638 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
2639 *
2640 * Returns a pointer to the method structure if variable is found.
2641 * Otherwise returns NULL. The method variable index is set in "*idx".
2642 */
2643 ufunc_T *
2644method_lookup(
2645 class_T *cl,
2646 vartype_T v_type,
2647 char_u *name,
2648 size_t namelen,
2649 int *idx)
2650{
2651 if (v_type == VAR_CLASS)
2652 return class_method_lookup(cl, name, namelen, idx);
2653 else
2654 return object_method_lookup(cl, name, namelen, idx);
2655}
2656
2657/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00002658 * Return TRUE if current context "cctx_arg" is inside class "cl".
2659 * Return FALSE if not.
2660 */
2661 int
2662inside_class(cctx_T *cctx_arg, class_T *cl)
2663{
2664 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002665 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002666 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002667 return TRUE;
2668 return FALSE;
2669}
2670
2671/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002672 * Make a copy of an object.
2673 */
2674 void
2675copy_object(typval_T *from, typval_T *to)
2676{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002677 if (from->vval.v_object == NULL)
2678 to->vval.v_object = NULL;
2679 else
2680 {
2681 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002682 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002683 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002684}
2685
2686/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002687 * Make a copy of a class.
2688 */
2689 void
2690copy_class(typval_T *from, typval_T *to)
2691{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002692 if (from->vval.v_class == NULL)
2693 to->vval.v_class = NULL;
2694 else
2695 {
2696 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002697 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002698 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002699}
2700
2701/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002702 * Free the class "cl" and its contents.
2703 */
2704 static void
2705class_free(class_T *cl)
2706{
2707 // Freeing what the class contains may recursively come back here.
2708 // Clear "class_name" first, if it is NULL the class does not need to
2709 // be freed.
2710 VIM_CLEAR(cl->class_name);
2711
2712 class_unref(cl->class_extends);
2713
2714 for (int i = 0; i < cl->class_interface_count; ++i)
2715 {
2716 vim_free(((char_u **)cl->class_interfaces)[i]);
2717 if (cl->class_interfaces_cl[i] != NULL)
2718 class_unref(cl->class_interfaces_cl[i]);
2719 }
2720 vim_free(cl->class_interfaces);
2721 vim_free(cl->class_interfaces_cl);
2722
2723 itf2class_T *next;
2724 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2725 {
2726 next = i2c->i2c_next;
2727 vim_free(i2c);
2728 }
2729
2730 for (int i = 0; i < cl->class_class_member_count; ++i)
2731 {
2732 ocmember_T *m = &cl->class_class_members[i];
2733 vim_free(m->ocm_name);
2734 vim_free(m->ocm_init);
2735 if (cl->class_members_tv != NULL)
2736 clear_tv(&cl->class_members_tv[i]);
2737 }
2738 vim_free(cl->class_class_members);
2739 vim_free(cl->class_members_tv);
2740
2741 for (int i = 0; i < cl->class_obj_member_count; ++i)
2742 {
2743 ocmember_T *m = &cl->class_obj_members[i];
2744 vim_free(m->ocm_name);
2745 vim_free(m->ocm_init);
2746 }
2747 vim_free(cl->class_obj_members);
2748
2749 for (int i = 0; i < cl->class_class_function_count; ++i)
2750 {
2751 ufunc_T *uf = cl->class_class_functions[i];
2752 func_clear_free(uf, FALSE);
2753 }
2754 vim_free(cl->class_class_functions);
2755
2756 for (int i = 0; i < cl->class_obj_method_count; ++i)
2757 {
2758 ufunc_T *uf = cl->class_obj_methods[i];
2759 func_clear_free(uf, FALSE);
2760 }
2761 vim_free(cl->class_obj_methods);
2762
2763 clear_type_list(&cl->class_type_list);
2764
2765 class_cleared(cl);
2766
2767 vim_free(cl);
2768}
2769
2770/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002771 * Unreference a class. Free it when the reference count goes down to zero.
2772 */
2773 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002774class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002775{
Bram Moolenaard505d172022-12-18 21:42:55 +00002776 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002777 class_free(cl);
2778}
2779
2780/*
2781 * Go through the list of all classes and free items without "copyID".
2782 */
2783 int
2784class_free_nonref(int copyID)
2785{
2786 int did_free = FALSE;
2787
2788 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002789 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002790 next_nonref_class = cl->class_next_used;
2791 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002792 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002793 // Free the class and items it contains.
2794 class_free(cl);
2795 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002796 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002797 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002798
2799 next_nonref_class = NULL;
2800 return did_free;
2801}
2802
2803 int
2804set_ref_in_classes(int copyID)
2805{
2806 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
2807 set_ref_in_item_class(cl, copyID, NULL, NULL);
2808
2809 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002810}
2811
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002812static object_T *first_object = NULL;
2813
2814/*
2815 * Call this function when an object has been created. It will be added to the
2816 * list headed by "first_object".
2817 */
2818 void
2819object_created(object_T *obj)
2820{
2821 if (first_object != NULL)
2822 {
2823 obj->obj_next_used = first_object;
2824 first_object->obj_prev_used = obj;
2825 }
2826 first_object = obj;
2827}
2828
2829/*
2830 * Call this function when an object has been cleared and is about to be freed.
2831 * It is removed from the list headed by "first_object".
2832 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002833 static void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002834object_cleared(object_T *obj)
2835{
2836 if (obj->obj_next_used != NULL)
2837 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
2838 if (obj->obj_prev_used != NULL)
2839 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
2840 else if (first_object == obj)
2841 first_object = obj->obj_next_used;
2842}
2843
2844/*
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002845 * Free the contents of an object ignoring the reference count.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002846 */
2847 static void
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002848object_free_contents(object_T *obj)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002849{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002850 class_T *cl = obj->obj_class;
2851
2852 if (!cl)
2853 return;
2854
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002855 // Avoid a recursive call, it can happen if "obj" has a circular reference.
2856 obj->obj_refcount = INT_MAX;
2857
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002858 // the member values are just after the object structure
2859 typval_T *tv = (typval_T *)(obj + 1);
2860 for (int i = 0; i < cl->class_obj_member_count; ++i)
2861 clear_tv(tv + i);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002862}
2863
2864 static void
2865object_free_object(object_T *obj)
2866{
2867 class_T *cl = obj->obj_class;
2868
2869 if (!cl)
2870 return;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002871
2872 // Remove from the list headed by "first_object".
2873 object_cleared(obj);
2874
2875 vim_free(obj);
2876 class_unref(cl);
2877}
2878
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002879 static void
2880object_free(object_T *obj)
2881{
2882 if (in_free_unref_items)
2883 return;
2884
2885 object_free_contents(obj);
2886 object_free_object(obj);
2887}
2888
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002889/*
2890 * Unreference an object.
2891 */
2892 void
2893object_unref(object_T *obj)
2894{
2895 if (obj != NULL && --obj->obj_refcount <= 0)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002896 object_free(obj);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002897}
2898
2899/*
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002900 * Go through the list of all objects and free items without "copyID".
2901 */
2902 int
2903object_free_nonref(int copyID)
2904{
2905 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002906
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002907 for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002908 {
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002909 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
2910 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002911 // Free the object contents. Object itself will be freed later.
2912 object_free_contents(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002913 did_free = TRUE;
2914 }
2915 }
2916
2917 return did_free;
2918}
2919
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002920 void
2921object_free_items(int copyID)
2922{
2923 object_T *obj_next;
2924
2925 for (object_T *obj = first_object; obj != NULL; obj = obj_next)
2926 {
2927 obj_next = obj->obj_next_used;
2928 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
2929 object_free_object(obj);
2930 }
2931}
2932
LemonBoyafe04662023-08-23 21:08:11 +02002933/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02002934 * Output message which takes a variable name and the class that defines it.
2935 * "cl" is that class where the name was found. Search "cl"'s hierarchy to
2936 * find the defining class.
2937 */
2938 void
2939emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl)
2940{
2941 ocmember_T *m;
2942 class_T *cl_def = class_defining_member(cl, name, len, &m);
2943 if (cl_def != NULL)
2944 semsg(_(msg), m->ocm_name, cl_def->class_name);
2945 else
2946 emsg(_(e_internal_error_please_report_a_bug));
2947}
2948
2949/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002950 * Echo a class or object method not found message.
2951 */
2952 void
2953method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
2954{
2955 char_u *method_name = vim_strnsave(name, len);
2956 if ((v_type == VAR_OBJECT)
2957 && (class_method_idx(cl, name, len) >= 0))
2958 {
2959 // If this is a class method, then give a different error
2960 if (*name == '_')
2961 semsg(_(e_cannot_access_private_method_str), method_name);
2962 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002963 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002964 method_name, cl->class_name);
2965 }
2966 else if ((v_type == VAR_CLASS)
2967 && (object_method_idx(cl, name, len) >= 0))
2968 {
2969 // If this is an object method, then give a different error
2970 if (*name == '_')
2971 semsg(_(e_cannot_access_private_method_str), method_name);
2972 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002973 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002974 method_name, cl->class_name);
2975 }
2976 else
2977 semsg(_(e_method_not_found_on_class_str_str), cl->class_name,
2978 method_name);
2979 vim_free(method_name);
2980}
2981
2982/*
2983 * Echo a class or object member not found message.
2984 */
2985 void
2986member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
2987{
2988 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
2989
2990 if (v_type == VAR_OBJECT)
2991 {
2992 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02002993 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002994 varname, cl->class_name);
2995 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002996 semsg(_(e_variable_not_found_on_object_str_str), cl->class_name,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002997 varname);
2998 }
2999 else
3000 {
3001 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003002 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003003 varname, cl->class_name);
3004 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003005 semsg(_(e_class_variable_str_not_found_in_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003006 varname, cl->class_name);
3007 }
3008 vim_free(varname);
3009}
3010
3011/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02003012 * Return TRUE when the class "cl", its base class or one of the implemented
3013 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02003014 */
3015 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003016class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02003017{
3018 if (cl == other_cl)
3019 return TRUE;
3020
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003021 // Recursively check the base classes.
3022 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02003023 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003024 if (cl == other_cl)
3025 return TRUE;
3026 // Check the implemented interfaces and the super interfaces
3027 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003028 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003029 class_T *intf = cl->class_interfaces_cl[i];
3030 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003031 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003032 if (intf == other_cl)
3033 return TRUE;
3034 // check the super interfaces
3035 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003036 }
3037 }
LemonBoyafe04662023-08-23 21:08:11 +02003038 }
3039
3040 return FALSE;
3041}
3042
3043/*
3044 * "instanceof(object, classinfo)" function
3045 */
3046 void
3047f_instanceof(typval_T *argvars, typval_T *rettv)
3048{
3049 typval_T *object_tv = &argvars[0];
3050 typval_T *classinfo_tv = &argvars[1];
3051 listitem_T *li;
3052
3053 rettv->vval.v_number = VVAL_FALSE;
3054
3055 if (check_for_object_arg(argvars, 0) == FAIL
3056 || check_for_class_or_list_arg(argvars, 1) == FAIL)
3057 return;
3058
Ernie Rael3da696d2023-09-19 20:14:18 +02003059 if (object_tv->vval.v_object == NULL)
3060 return;
3061
LemonBoyafe04662023-08-23 21:08:11 +02003062 if (classinfo_tv->v_type == VAR_LIST)
3063 {
3064 FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
3065 {
3066 if (li->li_tv.v_type != VAR_CLASS)
3067 {
3068 emsg(_(e_class_required));
3069 return;
3070 }
3071
3072 if (class_instance_of(object_tv->vval.v_object->obj_class,
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003073 li->li_tv.vval.v_class) == TRUE)
LemonBoyafe04662023-08-23 21:08:11 +02003074 {
3075 rettv->vval.v_number = VVAL_TRUE;
3076 return;
3077 }
3078 }
3079 }
3080 else if (classinfo_tv->v_type == VAR_CLASS)
3081 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003082 rettv->vval.v_number = class_instance_of(object_tv->vval.v_object->obj_class,
3083 classinfo_tv->vval.v_class);
LemonBoyafe04662023-08-23 21:08:11 +02003084 }
3085}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00003086
3087#endif // FEAT_EVAL