blob: ce231600442fea73ae7b315dd5ee043091d493a7 [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,
558 int is_class_var,
559 ocmember_T *cl_mt,
560 int cl_member_count,
561 class_T *extends_cl)
562{
563 int variable_present = FALSE;
564
565 for (int cl_i = 0; cl_i < cl_member_count; ++cl_i)
566 {
567 ocmember_T *m = &cl_mt[cl_i];
568 where_T where = WHERE_INIT;
569
570 if (STRCMP(if_var->ocm_name, m->ocm_name) != 0)
571 continue;
572
573 // Ensure the access type is same
574 if (if_var->ocm_access != m->ocm_access)
575 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200576 semsg(_(e_variable_str_of_interface_str_has_different_access),
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200577 if_var->ocm_name, intf_class_name);
578 return FALSE;
579 }
580
581 // Ensure the type is matching.
582 if (m->ocm_type == &t_any)
583 {
584 // variable type is not specified. Use the variable type in the
585 // interface.
586 m->ocm_type = if_var->ocm_type;
587 }
588 else
589 {
590 where.wt_func_name = (char *)m->ocm_name;
591 where.wt_kind = WT_MEMBER;
592 if (check_type(if_var->ocm_type, m->ocm_type, TRUE,
593 where) == FAIL)
594 return FALSE;
595 }
596
597 variable_present = TRUE;
598 break;
599 }
600
601 if (!variable_present && extends_cl != NULL)
602 {
603 int ext_cl_count = is_class_var
604 ? extends_cl->class_class_member_count
605 : extends_cl->class_obj_member_count;
606 ocmember_T *ext_cl_mt = is_class_var
607 ? extends_cl->class_class_members
608 : extends_cl->class_obj_members;
609 return intf_variable_present(intf_class_name, if_var,
610 is_class_var, ext_cl_mt,
611 ext_cl_count,
612 extends_cl->class_extends);
613 }
614
615 return variable_present;
616}
617
618/*
619 * Check the variables of the interface class "ifcl" match the class variables
620 * ("classmembers_gap") and object variables ("objmembers_gap") of a class.
621 * Returns TRUE if the class and object variables names are valid.
622 */
623 static int
624validate_interface_variables(
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200625 char_u *intf_class_name,
626 class_T *ifcl,
627 garray_T *classmembers_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200628 garray_T *objmembers_gap,
629 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200630{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200631 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200632 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200633 // loop == 1: check class variables
634 // loop == 2: check object variables
635 int is_class_var = (loop == 1);
636 int if_count = is_class_var ? ifcl->class_class_member_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200637 : ifcl->class_obj_member_count;
638 if (if_count == 0)
639 continue;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200640 ocmember_T *if_ms = is_class_var ? ifcl->class_class_members
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200641 : ifcl->class_obj_members;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200642 ocmember_T *cl_ms = (ocmember_T *)(is_class_var
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200643 ? classmembers_gap->ga_data
644 : objmembers_gap->ga_data);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200645 int cl_count = is_class_var ? classmembers_gap->ga_len
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200646 : objmembers_gap->ga_len;
647 for (int if_i = 0; if_i < if_count; ++if_i)
648 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200649 if (!intf_variable_present(intf_class_name, &if_ms[if_i],
650 is_class_var, cl_ms, cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200651 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200652 semsg(_(e_variable_str_of_interface_str_not_implemented),
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200653 if_ms[if_i].ocm_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200654 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200655 }
656 }
657 }
658
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200659 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200660}
661
662/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200663 * Returns TRUE if the method signature of "if_method" and "cl_method" matches.
664 */
665 static int
666intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method)
667{
668 where_T where = WHERE_INIT;
669
670 // Ensure the type is matching.
671 where.wt_func_name = (char *)if_method->uf_name;
672 where.wt_kind = WT_METHOD;
673 if (check_type(if_method->uf_func_type, cl_method->uf_func_type, TRUE,
674 where) == FAIL)
675 return FALSE;
676
677 return TRUE;
678}
679
680/*
681 * Returns TRUE if the interface method "if_ufunc" is present in the list of
682 * methods in "cl_fp" or in the parent lineage of one of the extended classes
683 * in "extends_cl". For a class method, 'is_class_method' is TRUE.
684 */
685 static int
686intf_method_present(
687 ufunc_T *if_ufunc,
688 int is_class_method,
689 ufunc_T **cl_fp,
690 int cl_count,
691 class_T *extends_cl)
692{
693 int method_present = FALSE;
694
695 for (int cl_i = 0; cl_i < cl_count; ++cl_i)
696 {
697 char_u *cl_name = cl_fp[cl_i]->uf_name;
698 if (STRCMP(if_ufunc->uf_name, cl_name) == 0)
699 {
700 // Ensure the type is matching.
701 if (!intf_method_type_matches(if_ufunc, cl_fp[cl_i]))
702 return FALSE;
703 method_present = TRUE;
704 break;
705 }
706 }
707
708 if (!method_present && extends_cl != NULL)
709 {
710 ufunc_T **ext_cl_fp = (ufunc_T **)(is_class_method
711 ? extends_cl->class_class_functions
712 : extends_cl->class_obj_methods);
713 int ext_cl_count = is_class_method
714 ? extends_cl->class_class_function_count
715 : extends_cl->class_obj_method_count;
716 return intf_method_present(if_ufunc, is_class_method, ext_cl_fp,
717 ext_cl_count,
718 extends_cl->class_extends);
719 }
720
721 return method_present;
722}
723
724/*
725 * Validate that a new class implements all the class/instance methods in the
726 * interface "ifcl". The new class methods are in "classfunctions_gap" and the
727 * new object methods are in "objmemthods_gap". Also validates the method
728 * types.
729 * Returns TRUE if all the interface class/object methods are implemented in
730 * the new class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200731 */
732 static int
733validate_interface_methods(
734 char_u *intf_class_name,
735 class_T *ifcl,
736 garray_T *classfunctions_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200737 garray_T *objmethods_gap,
738 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200739{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200740 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200741 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200742 // loop == 1: check class methods
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200743 // loop == 2: check object methods
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200744 int is_class_method = (loop == 1);
745 int if_count = is_class_method ? ifcl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200746 : ifcl->class_obj_method_count;
747 if (if_count == 0)
748 continue;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200749 ufunc_T **if_fp = is_class_method ? ifcl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200750 : ifcl->class_obj_methods;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200751 ufunc_T **cl_fp = (ufunc_T **)(is_class_method
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200752 ? classfunctions_gap->ga_data
753 : objmethods_gap->ga_data);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200754 int cl_count = is_class_method ? classfunctions_gap->ga_len
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200755 : objmethods_gap->ga_len;
756 for (int if_i = 0; if_i < if_count; ++if_i)
757 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200758 char_u *if_name = if_fp[if_i]->uf_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200759
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200760 if (!intf_method_present(if_fp[if_i], is_class_method, cl_fp,
761 cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200762 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200763 semsg(_(e_method_str_of_interface_str_not_implemented),
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200764 if_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200765 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200766 }
767 }
768 }
769
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200770 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200771}
772
773/*
774 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200775 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200776 * object methods and object members in the new class are in
777 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
778 * "objmembers_gap" respectively.
779 */
780 static int
781validate_implements_classes(
782 garray_T *impl_gap,
783 class_T **intf_classes,
784 garray_T *classfunctions_gap,
785 garray_T *classmembers_gap,
786 garray_T *objmethods_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200787 garray_T *objmembers_gap,
788 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200789{
790 int success = TRUE;
791
792 for (int i = 0; i < impl_gap->ga_len && success; ++i)
793 {
794 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
795 typval_T tv;
796 tv.v_type = VAR_UNKNOWN;
797 if (eval_variable_import(impl, &tv) == FAIL)
798 {
799 semsg(_(e_interface_name_not_found_str), impl);
800 success = FALSE;
801 break;
802 }
803
804 if (tv.v_type != VAR_CLASS
805 || tv.vval.v_class == NULL
806 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
807 {
808 semsg(_(e_not_valid_interface_str), impl);
809 success = FALSE;
810 clear_tv(&tv);
811 break;
812 }
813
814 class_T *ifcl = tv.vval.v_class;
815 intf_classes[i] = ifcl;
816 ++ifcl->class_refcount;
817
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200818 // check the variables of the interface match the members of the class
819 success = validate_interface_variables(impl, ifcl, classmembers_gap,
820 objmembers_gap, extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200821
822 // check the functions/methods of the interface match the
823 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200824 if (success)
825 success = validate_interface_methods(impl, ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200826 classfunctions_gap, objmethods_gap,
827 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200828 clear_tv(&tv);
829 }
830
831 return success;
832}
833
834/*
835 * Check no function argument name is used as a class member.
836 * (Object members are always accessed with "this." prefix, so no need
837 * to check them.)
838 */
839 static int
840check_func_arg_names(
841 garray_T *classfunctions_gap,
842 garray_T *objmethods_gap,
843 garray_T *classmembers_gap)
844{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200845 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200846 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200847 {
848 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
849
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200850 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200851 {
852 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
853
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200854 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200855 {
856 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
857 garray_T *mgap = classmembers_gap;
858
859 // Check all the class member names
860 for (int mi = 0; mi < mgap->ga_len; ++mi)
861 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200862 char_u *mname =
863 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200864 if (STRCMP(aname, mname) == 0)
865 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200866 if (uf->uf_script_ctx.sc_sid > 0)
867 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
868
869 semsg(_(e_argument_already_declared_in_class_str),
870 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200871
872 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200873 }
874 }
875 }
876 }
877 }
878
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200879 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200880}
881
882/*
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200883 * Returns TRUE if 'varname' is a reserved keyword name
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200884 */
885 static int
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200886is_reserved_varname(char_u *varname, char_u *varname_end)
887{
888 int reserved = FALSE;
889 char_u save_varname_end = *varname_end;
890 *varname_end = NUL;
891
892 reserved = check_reserved_name(varname, FALSE) == FAIL;
893
894 *varname_end = save_varname_end;
895
896 return reserved;
897}
898
899/*
900 * Returns TRUE if the variable "varname" is already defined either as a class
901 * variable or as an object variable.
902 */
903 static int
904is_duplicate_variable(
905 garray_T *class_members,
906 garray_T *obj_members,
907 char_u *varname,
908 char_u *varname_end)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200909{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200910 char_u *name = vim_strnsave(varname, varname_end - varname);
911 char_u *pstr = (*name == '_') ? name + 1 : name;
912 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200913
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200914 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200915 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200916 // loop == 1: class variables, loop == 2: object variables
917 garray_T *vgap = (loop == 1) ? class_members : obj_members;
918 for (int i = 0; i < vgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200919 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200920 ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i;
921 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1
922 : m->ocm_name;
923 if (STRCMP(pstr, qstr) == 0)
924 {
925 semsg(_(e_duplicate_variable_str), name);
926 dup = TRUE;
927 break;
928 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200929 }
930 }
931
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200932 vim_free(name);
933 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200934}
935
936/*
937 * Returns TRUE if the method "name" is already defined.
938 */
939 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200940is_duplicate_method(
941 garray_T *classmethods_gap,
942 garray_T *objmethods_gap,
943 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200944{
945 char_u *pstr = (*name == '_') ? name + 1 : name;
946
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200947 // loop 1: class methods, loop 2: object methods
948 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200949 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200950 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
951 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200952 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200953 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
954 char_u *qstr = *n == '_' ? n + 1 : n;
955 if (STRCMP(pstr, qstr) == 0)
956 {
957 semsg(_(e_duplicate_function_str), name);
958 return TRUE;
959 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200960 }
961 }
962
963 return FALSE;
964}
965
966/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200967 * Returns TRUE if the constructor is valid.
968 */
969 static int
970is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
971{
972 // Constructors are not allowed in abstract classes.
973 if (is_abstract)
974 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200975 emsg(_(e_cannot_define_new_method_in_abstract_class));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200976 return FALSE;
977 }
978 // A constructor is always static, no need to define it so.
979 if (has_static)
980 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200981 emsg(_(e_cannot_define_new_method_as_static));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200982 return FALSE;
983 }
984 // A return type should not be specified for the new()
985 // constructor method.
986 if (uf->uf_ret_type->tt_type != VAR_VOID)
987 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200988 emsg(_(e_cannot_use_a_return_type_with_new_method));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200989 return FALSE;
990 }
991 return TRUE;
992}
993
994/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200995 * Update the interface class lookup table for the member index on the
996 * interface to the member index in the class implementing the interface.
997 * And a lookup table for the object method index on the interface
998 * to the object method index in the class implementing the interface.
999 * This is also used for updating the lookup table for the extended class
1000 * hierarchy.
1001 */
1002 static int
1003update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001004 class_T *ifcl,
1005 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001006 garray_T *objmethods,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001007 int pobj_method_offset)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001008{
1009 if (ifcl == NULL)
1010 return OK;
1011
1012 // Table for members.
1013 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001014 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001015 if (if2cl == NULL)
1016 return FAIL;
1017 if2cl->i2c_next = ifcl->class_itf2class;
1018 ifcl->class_itf2class = if2cl;
1019 if2cl->i2c_class = cl;
1020 if2cl->i2c_is_method = FALSE;
1021
1022 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
1023 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
1024 {
1025 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001026 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001027 {
1028 int *table = (int *)(if2cl + 1);
1029 table[if_i] = cl_i;
1030 break;
1031 }
1032 }
1033
1034 // Table for methods.
1035 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001036 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001037 if (if2cl == NULL)
1038 return FAIL;
1039 if2cl->i2c_next = ifcl->class_itf2class;
1040 ifcl->class_itf2class = if2cl;
1041 if2cl->i2c_class = cl;
1042 if2cl->i2c_is_method = TRUE;
1043
1044 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
1045 {
1046 int done = FALSE;
1047 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
1048 {
1049 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001050 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001051 {
1052 int *table = (int *)(if2cl + 1);
1053 table[if_i] = cl_i;
1054 done = TRUE;
1055 break;
1056 }
1057 }
1058
1059 // extended class object method is not overridden by the child class.
1060 // Keep the method declared in one of the parent classes in the
1061 // lineage.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001062 if (!done)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001063 {
1064 // If "ifcl" is not the immediate parent of "cl", then search in
1065 // the intermediate parent classes.
1066 if (cl->class_extends != ifcl)
1067 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001068 class_T *parent = cl->class_extends;
1069 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001070
1071 while (!done && parent != NULL && parent != ifcl)
1072 {
1073
1074 for (int cl_i = 0;
1075 cl_i < parent->class_obj_method_count_child; ++cl_i)
1076 {
1077 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
1078 parent->class_obj_methods[cl_i]->uf_name)
1079 == 0)
1080 {
1081 int *table = (int *)(if2cl + 1);
1082 table[if_i] = method_offset + cl_i;
1083 done = TRUE;
1084 break;
1085 }
1086 }
1087 method_offset += parent->class_obj_method_count_child;
1088 parent = parent->class_extends;
1089 }
1090 }
1091
1092 if (!done)
1093 {
1094 int *table = (int *)(if2cl + 1);
1095 table[if_i] = pobj_method_offset + if_i;
1096 }
1097 }
1098 }
1099
1100 return OK;
1101}
1102
1103/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001104 * Update the member and object method lookup tables for a new class in the
1105 * interface class.
1106 * For each interface add a lookup table for the member index on the interface
1107 * to the member index in the new class. And a lookup table for the object
1108 * method index on the interface to the object method index in the new class.
1109 */
1110 static int
1111add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
1112{
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001113 // update the lookup table for all the implemented interfaces
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001114 for (int i = 0; i < cl->class_interface_count; ++i)
1115 {
1116 class_T *ifcl = cl->class_interfaces_cl[i];
1117
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001118 // update the lookup table for this interface and all its super
1119 // interfaces.
1120 while (ifcl != NULL)
1121 {
1122 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
1123 0) == FAIL)
1124 return FAIL;
1125 ifcl = ifcl->class_extends;
1126 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001127 }
1128
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001129 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001130 if (extends_cl != NULL)
1131 {
1132 class_T *pclass = extends_cl;
1133 int pobj_method_offset = objmethods_gap->ga_len;
1134
1135 // Update the entire lineage of extended classes.
1136 while (pclass != NULL)
1137 {
1138 if (update_member_method_lookup_table(pclass, cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001139 objmethods_gap, pobj_method_offset) == FAIL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001140 return FAIL;
1141
1142 pobj_method_offset += pclass->class_obj_method_count_child;
1143 pclass = pclass->class_extends;
1144 }
1145 }
1146
1147 return OK;
1148}
1149
1150/*
1151 * Add class members to a new class. Allocate a typval for each class member
1152 * and initialize it.
1153 */
1154 static void
1155add_class_members(class_T *cl, exarg_T *eap)
1156{
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001157 garray_T type_list;
1158
1159 ga_init2(&type_list, sizeof(type_T *), 10);
1160
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001161 // Allocate a typval for each class member and initialize it.
1162 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
1163 cl->class_class_member_count);
1164 if (cl->class_members_tv == NULL)
1165 return;
1166
1167 for (int i = 0; i < cl->class_class_member_count; ++i)
1168 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001169 ocmember_T *m = &cl->class_class_members[i];
1170 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001171 if (m->ocm_init != NULL)
1172 {
1173 typval_T *etv = eval_expr(m->ocm_init, eap);
1174 if (etv != NULL)
1175 {
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001176 if (m->ocm_type->tt_type == VAR_ANY
1177 && !m->ocm_has_type
1178 && etv->v_type != VAR_SPECIAL)
1179 // If the member variable type is not yet set, then use
1180 // the initialization expression type.
1181 m->ocm_type = typval2type(etv, get_copyID(), &type_list,
1182 TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001183 *tv = *etv;
1184 vim_free(etv);
1185 }
1186 }
1187 else
1188 {
1189 // TODO: proper default value
1190 tv->v_type = m->ocm_type->tt_type;
1191 tv->vval.v_string = NULL;
1192 }
1193 }
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001194
1195 clear_type_list(&type_list);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001196}
1197
1198/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001199 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001200 */
1201 static void
1202add_default_constructor(
1203 class_T *cl,
1204 garray_T *classfunctions_gap,
1205 garray_T *type_list_gap)
1206{
1207 garray_T fga;
1208
1209 ga_init2(&fga, 1, 1000);
1210 ga_concat(&fga, (char_u *)"new(");
1211 for (int i = 0; i < cl->class_obj_member_count; ++i)
1212 {
1213 if (i > 0)
1214 ga_concat(&fga, (char_u *)", ");
1215 ga_concat(&fga, (char_u *)"this.");
1216 ocmember_T *m = cl->class_obj_members + i;
1217 ga_concat(&fga, (char_u *)m->ocm_name);
1218 ga_concat(&fga, (char_u *)" = v:none");
1219 }
1220 ga_concat(&fga, (char_u *)")\nenddef\n");
1221 ga_append(&fga, NUL);
1222
1223 exarg_T fea;
1224 CLEAR_FIELD(fea);
1225 fea.cmdidx = CMD_def;
1226 fea.cmd = fea.arg = fga.ga_data;
1227
1228 garray_T lines_to_free;
1229 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1230
h-eastb895b0f2023-09-24 15:46:31 +02001231 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
1232 cl->class_obj_members, cl->class_obj_member_count);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001233
1234 ga_clear_strings(&lines_to_free);
1235 vim_free(fga.ga_data);
1236
1237 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1238 {
1239 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001240 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001241 ++classfunctions_gap->ga_len;
1242
1243 nf->uf_flags |= FC_NEW;
1244 nf->uf_ret_type = get_type_ptr(type_list_gap);
1245 if (nf->uf_ret_type != NULL)
1246 {
1247 nf->uf_ret_type->tt_type = VAR_OBJECT;
1248 nf->uf_ret_type->tt_class = cl;
1249 nf->uf_ret_type->tt_argcount = 0;
1250 nf->uf_ret_type->tt_args = NULL;
1251 }
1252 }
1253}
1254
1255/*
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001256 * Add the class methods and object methods to the new class "cl".
1257 * When extending a class "extends_cl", add the instance methods from the
1258 * parent class also.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001259 */
1260 static int
1261add_classfuncs_objmethods(
1262 class_T *cl,
1263 class_T *extends_cl,
1264 garray_T *classfunctions_gap,
1265 garray_T *objmethods_gap)
1266{
1267 // loop 1: class functions, loop 2: object methods
1268 for (int loop = 1; loop <= 2; ++loop)
1269 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001270 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1271 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001272 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001273 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001274 : &cl->class_obj_methods;
1275
1276 int parent_count = 0;
1277 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001278 // Include object methods from the parent.
1279 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001280 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001281 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001282 : extends_cl->class_obj_method_count;
1283
1284 *fcount = parent_count + gap->ga_len;
1285 if (*fcount == 0)
1286 {
1287 *fup = NULL;
1288 continue;
1289 }
1290 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1291 if (*fup == NULL)
1292 return FAIL;
1293
1294 if (gap->ga_len != 0)
1295 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
1296 vim_free(gap->ga_data);
1297 if (loop == 1)
1298 cl->class_class_function_count_child = gap->ga_len;
1299 else
1300 cl->class_obj_method_count_child = gap->ga_len;
1301
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001302 if (loop == 2)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001303 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001304 // Copy instance methods from the parent.
1305
1306 for (int i = 0; i < parent_count; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001307 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001308 // Can't use the same parent function, because "uf_class" is
1309 // different and compilation will have a different result.
1310 // Put them after the functions in the current class, object
1311 // methods may be overruled, then "super.Method()" is used to
1312 // find a method from the parent.
1313 ufunc_T *pf = (extends_cl->class_obj_methods)[i];
1314 (*fup)[gap->ga_len + i] = copy_function(pf);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001315
1316 // If the child class overrides a function from the parent
1317 // the signature must be equal.
1318 char_u *pname = pf->uf_name;
1319 for (int ci = 0; ci < gap->ga_len; ++ci)
1320 {
1321 ufunc_T *cf = (*fup)[ci];
1322 char_u *cname = cf->uf_name;
1323 if (STRCMP(pname, cname) == 0)
1324 {
1325 where_T where = WHERE_INIT;
1326 where.wt_func_name = (char *)pname;
1327 where.wt_kind = WT_METHOD;
1328 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1329 TRUE, where);
1330 }
1331 }
1332 }
1333 }
1334
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001335 // Set the class pointer on all the functions and object methods.
1336 for (int i = 0; i < *fcount; ++i)
1337 {
1338 ufunc_T *fp = (*fup)[i];
1339 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001340 if (i < gap->ga_len)
1341 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001342 if (loop == 2)
1343 fp->uf_flags |= FC_OBJECT;
1344 }
1345 }
1346
1347 return OK;
1348}
1349
1350/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001351 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001352 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001353 */
1354 void
1355ex_class(exarg_T *eap)
1356{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001357 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1358 long start_lnum = SOURCING_LNUM;
1359 char_u *arg = eap->arg;
1360 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001361
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001362 if (is_abstract)
1363 {
1364 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1365 {
1366 semsg(_(e_invalid_argument_str), arg);
1367 return;
1368 }
1369 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001370 is_class = TRUE;
1371 }
1372
1373 if (!current_script_is_vim9()
1374 || (cmdmod.cmod_flags & CMOD_LEGACY)
1375 || !getline_equal(eap->getline, eap->cookie, getsourceline))
1376 {
1377 if (is_class)
1378 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1379 else
1380 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1381 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001382 }
1383
1384 if (!ASCII_ISUPPER(*arg))
1385 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001386 if (is_class)
1387 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1388 else
1389 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1390 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001391 return;
1392 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001393 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1394 if (!IS_WHITE_OR_NUL(*name_end))
1395 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001396 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001397 return;
1398 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001399 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001400
Bram Moolenaara86655a2023-01-12 17:06:27 +00001401 // "export class" gets used when creating the class, don't use "is_export"
1402 // for the items inside the class.
1403 int class_export = is_export;
1404 is_export = FALSE;
1405
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001406 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001407 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001408
Bram Moolenaar83677162023-01-08 19:54:10 +00001409 // Name for "extends BaseClass"
1410 char_u *extends = NULL;
1411
Bram Moolenaar94674f22023-01-06 18:42:20 +00001412 // Names for "implements SomeInterface"
1413 garray_T ga_impl;
1414 ga_init2(&ga_impl, sizeof(char_u *), 5);
1415
1416 arg = skipwhite(name_end);
1417 while (*arg != NUL && *arg != '#' && *arg != '\n')
1418 {
1419 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001420 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001421 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1422 {
1423 if (extends != NULL)
1424 {
1425 emsg(_(e_duplicate_extends));
1426 goto early_ret;
1427 }
1428 arg = skipwhite(arg + 7);
1429 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1430 if (!IS_WHITE_OR_NUL(*end))
1431 {
1432 semsg(_(e_white_space_required_after_name_str), arg);
1433 goto early_ret;
1434 }
1435 extends = vim_strnsave(arg, end - arg);
1436 if (extends == NULL)
1437 goto early_ret;
1438
1439 arg = skipwhite(end + 1);
1440 }
1441 else if (STRNCMP(arg, "implements", 10) == 0
1442 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001443 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001444 if (!is_class)
1445 {
1446 emsg(_(e_interface_cannot_use_implements));
1447 goto early_ret;
1448 }
1449
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001450 if (ga_impl.ga_len > 0)
1451 {
1452 emsg(_(e_duplicate_implements));
1453 goto early_ret;
1454 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001455 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001456
1457 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001458 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001459 char_u *impl_end = find_name_end(arg, NULL, NULL,
1460 FNE_CHECK_START);
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001461 if ((!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1462 || (*impl_end == ','
1463 && !IS_WHITE_OR_NUL(*(impl_end + 1))))
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001464 {
1465 semsg(_(e_white_space_required_after_name_str), arg);
1466 goto early_ret;
1467 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001468 if (impl_end - arg == 0)
1469 {
1470 emsg(_(e_missing_name_after_implements));
1471 goto early_ret;
1472 }
1473
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001474 char_u *iname = vim_strnsave(arg, impl_end - arg);
1475 if (iname == NULL)
1476 goto early_ret;
1477 for (int i = 0; i < ga_impl.ga_len; ++i)
1478 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1479 {
1480 semsg(_(e_duplicate_interface_after_implements_str),
1481 iname);
1482 vim_free(iname);
1483 goto early_ret;
1484 }
1485 if (ga_add_string(&ga_impl, iname) == FAIL)
1486 {
1487 vim_free(iname);
1488 goto early_ret;
1489 }
1490 if (*impl_end != ',')
1491 {
1492 arg = skipwhite(impl_end);
1493 break;
1494 }
1495 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001496 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001497 }
1498 else
1499 {
1500 semsg(_(e_trailing_characters_str), arg);
1501early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001502 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001503 ga_clear_strings(&ga_impl);
1504 return;
1505 }
1506 }
1507
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001508 garray_T type_list; // list of pointers to allocated types
1509 ga_init2(&type_list, sizeof(type_T *), 10);
1510
Bram Moolenaard505d172022-12-18 21:42:55 +00001511 // Growarray with class members declared in the class.
1512 garray_T classmembers;
1513 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1514
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001515 // Growarray with functions declared in the class.
1516 garray_T classfunctions;
1517 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001518
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001519 // Growarray with object members declared in the class.
1520 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001521 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001522
1523 // Growarray with object methods declared in the class.
1524 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001525 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001526
1527 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001528 * Go over the body of the class/interface until "endclass" or
1529 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001530 */
1531 char_u *theline = NULL;
1532 int success = FALSE;
1533 for (;;)
1534 {
1535 vim_free(theline);
1536 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
1537 if (theline == NULL)
1538 break;
1539 char_u *line = skipwhite(theline);
1540
Bram Moolenaar418b5472022-12-20 13:38:22 +00001541 // Skip empty and comment lines.
1542 if (*line == NUL)
1543 continue;
1544 if (*line == '#')
1545 {
1546 if (vim9_bad_comment(line))
1547 break;
1548 continue;
1549 }
1550
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001551 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001552 char *end_name = is_class ? "endclass" : "endinterface";
1553 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001554 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001555 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001556 semsg(_(e_command_cannot_be_shortened_str), line);
1557 else if (*p == '|' || !ends_excmd2(line, p))
1558 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001559 else
1560 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001561 break;
1562 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001563 char *wrong_name = is_class ? "endinterface" : "endclass";
1564 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1565 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001566 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001567 break;
1568 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001569
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001570 int has_public = FALSE;
1571 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001572 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001573 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001574 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001575 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001576 break;
1577 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001578 if (!is_class)
1579 {
Yegappan Lakshmananb90e3bc2023-09-28 23:06:48 +02001580 emsg(_(e_public_variable_not_supported_in_interface));
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001581 break;
1582 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001583 has_public = TRUE;
1584 p = skipwhite(line + 6);
1585
Bram Moolenaard505d172022-12-18 21:42:55 +00001586 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001587 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001588 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001589 break;
1590 }
1591 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001592
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001593 int abstract_method = FALSE;
1594 char_u *pa = p;
1595 if (checkforcmd(&p, "abstract", 3))
1596 {
1597 if (STRNCMP(pa, "abstract", 8) != 0)
1598 {
1599 semsg(_(e_command_cannot_be_shortened_str), pa);
1600 break;
1601 }
1602
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001603 if (!is_class)
1604 // ignore "abstract" in an interface (as all the methods in an
1605 // interface are abstract.
1606 p = skipwhite(pa + 8);
1607 else
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001608 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001609 if (!is_abstract)
1610 {
1611 semsg(_(e_abstract_method_in_concrete_class), pa);
1612 break;
1613 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001614
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001615 abstract_method = TRUE;
1616 p = skipwhite(pa + 8);
1617 if (STRNCMP(p, "def", 3) != 0 && STRNCMP(p, "static", 6) != 0)
1618 {
1619 emsg(_(e_abstract_must_be_followed_by_def_or_static));
1620 break;
1621 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001622 }
1623 }
1624
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001625 int has_static = FALSE;
1626 char_u *ps = p;
1627 if (checkforcmd(&p, "static", 4))
1628 {
1629 if (STRNCMP(ps, "static", 6) != 0)
1630 {
1631 semsg(_(e_command_cannot_be_shortened_str), ps);
1632 break;
1633 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001634
1635 if (!is_class)
1636 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001637 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001638 break;
1639 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001640 has_static = TRUE;
1641 p = skipwhite(ps + 6);
1642 }
1643
Bram Moolenaard505d172022-12-18 21:42:55 +00001644 // object members (public, read access, private):
1645 // "this._varname"
1646 // "this.varname"
1647 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001648 if (STRNCMP(p, "this", 4) == 0)
1649 {
1650 if (p[4] != '.' || !eval_isnamec1(p[5]))
1651 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001652 semsg(_(e_invalid_object_variable_declaration_str), p);
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001653 break;
1654 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001655 if (has_static)
1656 {
1657 emsg(_(e_static_cannot_be_followed_by_this));
1658 break;
1659 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001660 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +00001661 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001662 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001663 char_u *init_expr = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001664 int has_type = FALSE;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001665
1666 if (!is_class && *varname == '_')
1667 {
1668 // private variables are not supported in an interface
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001669 semsg(_(e_private_variable_not_supported_in_interface),
1670 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001671 break;
1672 }
1673
Bram Moolenaard505d172022-12-18 21:42:55 +00001674 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001675 &varname_end, &has_type, &type_list, &type,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001676 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001677 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001678 if (is_reserved_varname(varname, varname_end))
1679 {
1680 vim_free(init_expr);
1681 break;
1682 }
1683 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1684 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001685 {
1686 vim_free(init_expr);
1687 break;
1688 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001689 if (add_member(&objmembers, varname, varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001690 has_public, has_type, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001691 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001692 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001693 break;
1694 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001695 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001696
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001697 // constructors:
1698 // def new()
1699 // enddef
1700 // def newOther()
1701 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001702 // object methods and class functions:
1703 // def SomeMethod()
1704 // enddef
1705 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001706 // enddef
1707 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001708 // def <Tval> someMethod()
1709 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001710 else if (checkforcmd(&p, "def", 3))
1711 {
1712 exarg_T ea;
1713 garray_T lines_to_free;
1714
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001715 if (has_public)
1716 {
1717 // "public" keyword is not supported when defining an object or
1718 // class method
1719 emsg(_(e_public_keyword_not_supported_for_method));
1720 break;
1721 }
1722
1723 if (*p == NUL)
1724 {
1725 // No method name following def
1726 semsg(_(e_not_valid_command_in_class_str), line);
1727 break;
1728 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001729
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001730 CLEAR_FIELD(ea);
1731 ea.cmd = line;
1732 ea.arg = p;
1733 ea.cmdidx = CMD_def;
1734 ea.getline = eap->getline;
1735 ea.cookie = eap->cookie;
1736
1737 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001738 int class_flags;
1739 if (is_class)
1740 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
1741 else
1742 class_flags = CF_INTERFACE;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001743 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02001744 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001745 ga_clear_strings(&lines_to_free);
1746
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001747 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001748 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001749 char_u *name = uf->uf_name;
1750 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001751
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001752 if (!is_class && *name == '_')
1753 {
1754 // private variables are not supported in an interface
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001755 semsg(_(e_private_method_not_supported_in_interface),
1756 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001757 func_clear_free(uf, FALSE);
1758 break;
1759 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001760 if (is_new && !is_valid_constructor(uf, is_abstract,
1761 has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001762 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001763 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001764 break;
1765 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001766
Bram Moolenaar58b40092023-01-11 15:59:05 +00001767 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001768 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001769 {
1770 success = FALSE;
1771 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001772 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001773 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001774
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001775 garray_T *fgap = has_static || is_new
1776 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001777 if (ga_grow(fgap, 1) == OK)
1778 {
1779 if (is_new)
1780 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001781
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001782 if (abstract_method)
1783 uf->uf_flags |= FC_ABSTRACT;
1784
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001785 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1786 ++fgap->ga_len;
1787 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001788 }
1789 }
1790
1791 // class members
1792 else if (has_static)
1793 {
1794 // class members (public, read access, private):
1795 // "static _varname"
1796 // "static varname"
1797 // "public static varname"
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001798 char_u *varname = p;
1799 char_u *varname_end = NULL;
1800 int has_type = FALSE;
1801 type_T *type = NULL;
1802 char_u *init_expr = NULL;
1803
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001804 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001805 &varname_end, &has_type, &type_list, &type,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001806 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001807 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001808 if (is_reserved_varname(varname, varname_end))
1809 {
1810 vim_free(init_expr);
1811 break;
1812 }
1813 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1814 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001815 {
1816 vim_free(init_expr);
1817 break;
1818 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001819 if (add_member(&classmembers, varname, varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001820 has_public, has_type, type, init_expr) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001821 {
1822 vim_free(init_expr);
1823 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001824 }
1825 }
1826
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001827 else
1828 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001829 if (is_class)
1830 semsg(_(e_not_valid_command_in_class_str), line);
1831 else
1832 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001833 break;
1834 }
1835 }
1836 vim_free(theline);
1837
Bram Moolenaar83677162023-01-08 19:54:10 +00001838 class_T *extends_cl = NULL; // class from "extends" argument
1839
1840 /*
1841 * Check a few things before defining the class.
1842 */
1843
1844 // Check the "extends" class is valid.
1845 if (success && extends != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001846 success = validate_extends_class(extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00001847 VIM_CLEAR(extends);
1848
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001849 // Check the new object methods to make sure their access (public or
1850 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001851 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001852 success = validate_extends_methods(&objmethods, extends_cl);
1853
1854 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001855 // variables in the extended class lineage. If an interface is extending
1856 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001857 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001858 {
1859 if (is_class)
1860 success = extends_check_dup_members(&objmembers, extends_cl);
1861 else
1862 success = extends_check_intf_var_type(&objmembers, extends_cl);
1863 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001864
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001865 // When extending an abstract class, make sure all the abstract methods in
1866 // the parent class are implemented. If the current class is an abstract
1867 // class, then there is no need for this check.
1868 if (success && !is_abstract && extends_cl != NULL
1869 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001870 success = validate_abstract_class_methods(&classfunctions,
1871 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001872
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001873 class_T **intf_classes = NULL;
1874
Bram Moolenaar83677162023-01-08 19:54:10 +00001875 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001876 if (success && ga_impl.ga_len > 0)
1877 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001878 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1879
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001880 success = validate_implements_classes(&ga_impl, intf_classes,
1881 &classfunctions, &classmembers,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001882 &objmethods, &objmembers,
1883 extends_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001884 }
1885
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001886 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001887 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001888 success = check_func_arg_names(&classfunctions, &objmethods,
1889 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001890
Bram Moolenaareb533502022-12-14 15:06:11 +00001891 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001892 if (success)
1893 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001894 // "endclass" encountered without failures: Create the class.
1895
Bram Moolenaareb533502022-12-14 15:06:11 +00001896 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001897 if (cl == NULL)
1898 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001899 if (!is_class)
1900 cl->class_flags = CLASS_INTERFACE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001901 else if (is_abstract)
1902 cl->class_flags = CLASS_ABSTRACT;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001903
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001904 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001905 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001906 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001907 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001908
Bram Moolenaard0200c82023-01-28 15:19:40 +00001909 if (extends_cl != NULL)
1910 {
1911 cl->class_extends = extends_cl;
1912 extends_cl->class_flags |= CLASS_EXTENDED;
1913 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001914
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001915 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00001916 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001917 NULL,
1918 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00001919 &cl->class_class_members,
1920 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001921 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001922 extends_cl == NULL ? NULL
1923 : extends_cl->class_obj_members,
1924 extends_cl == NULL ? 0
1925 : extends_cl->class_obj_member_count,
1926 &cl->class_obj_members,
1927 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001928 goto cleanup;
1929
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001930 if (ga_impl.ga_len > 0)
1931 {
1932 // Move the "implements" names into the class.
1933 cl->class_interface_count = ga_impl.ga_len;
1934 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1935 if (cl->class_interfaces == NULL)
1936 goto cleanup;
1937 for (int i = 0; i < ga_impl.ga_len; ++i)
1938 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1939 VIM_CLEAR(ga_impl.ga_data);
1940 ga_impl.ga_len = 0;
1941
Bram Moolenaard0200c82023-01-28 15:19:40 +00001942 cl->class_interfaces_cl = intf_classes;
1943 intf_classes = NULL;
1944 }
1945
1946 if (cl->class_interface_count > 0 || extends_cl != NULL)
1947 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001948 // Add a method and member lookup table to each of the interface
1949 // classes.
1950 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1951 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001952 }
1953
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001954 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001955 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001956 add_class_members(cl, eap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001957
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001958 int have_new = FALSE;
1959 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001960 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001961 {
1962 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1963 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001964 {
1965 have_new = TRUE;
1966 break;
1967 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001968 }
1969
1970 if (have_new)
1971 // The return type of new() is an object of class "cl"
1972 class_func->uf_ret_type->tt_class = cl;
1973 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001974 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001975 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001976
Bram Moolenaar58b40092023-01-11 15:59:05 +00001977 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001978 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1979 &objmethods) == FAIL)
1980 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001981
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001982 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001983 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001984 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001985 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001986 cl->class_type_list = type_list;
1987
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001988 class_created(cl);
1989
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001990 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001991 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001992
1993 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001994 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02001995 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001996 typval_T tv;
1997 tv.v_type = VAR_CLASS;
1998 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001999 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00002000 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002001 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00002002 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002003 return;
2004 }
2005
2006cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00002007 if (cl != NULL)
2008 {
2009 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002010 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002011 if (cl->class_interfaces != NULL)
2012 {
2013 for (int i = 0; i < cl->class_interface_count; ++i)
2014 vim_free(cl->class_interfaces[i]);
2015 vim_free(cl->class_interfaces);
2016 }
2017 if (cl->class_interfaces_cl != NULL)
2018 {
2019 for (int i = 0; i < cl->class_interface_count; ++i)
2020 class_unref(cl->class_interfaces_cl[i]);
2021 vim_free(cl->class_interfaces_cl);
2022 }
Bram Moolenaareb533502022-12-14 15:06:11 +00002023 vim_free(cl->class_obj_members);
2024 vim_free(cl->class_obj_methods);
2025 vim_free(cl);
2026 }
2027
Bram Moolenaar83677162023-01-08 19:54:10 +00002028 vim_free(extends);
2029 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002030
2031 if (intf_classes != NULL)
2032 {
2033 for (int i = 0; i < ga_impl.ga_len; ++i)
2034 class_unref(intf_classes[i]);
2035 vim_free(intf_classes);
2036 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00002037 ga_clear_strings(&ga_impl);
2038
Bram Moolenaard505d172022-12-18 21:42:55 +00002039 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002040 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002041 garray_T *gap = round == 1 ? &classmembers : &objmembers;
2042 if (gap->ga_len == 0 || gap->ga_data == NULL)
2043 continue;
2044
2045 for (int i = 0; i < gap->ga_len; ++i)
2046 {
2047 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2048 vim_free(m->ocm_name);
2049 vim_free(m->ocm_init);
2050 }
2051 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002052 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002053
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002054 for (int i = 0; i < objmethods.ga_len; ++i)
2055 {
2056 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2057 func_clear_free(uf, FALSE);
2058 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002059 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002060
2061 for (int i = 0; i < classfunctions.ga_len; ++i)
2062 {
2063 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2064 func_clear_free(uf, FALSE);
2065 }
2066 ga_clear(&classfunctions);
2067
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002068 clear_type_list(&type_list);
2069}
2070
2071/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002072 * Find member "name" in class "cl", set "member_idx" to the member index and
2073 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002074 * When "is_object" is TRUE, then look for object members. Otherwise look for
2075 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002076 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002077 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002078 */
2079 type_T *
2080class_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002081 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002082 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002083 char_u *name,
2084 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002085 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002086{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002087 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002088 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002089
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002090 *member_idx = -1; // not found (yet)
2091
2092 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2093 member_idx);
2094 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002095 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002096 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2097 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002098 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002099 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002100
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002101 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002102}
2103
2104/*
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002105 * Given a class or object variable index, return the variable type
2106 */
2107 type_T *
2108class_member_type_by_idx(
2109 class_T *cl,
2110 int is_object,
2111 int member_idx)
2112{
2113 ocmember_T *m;
2114 int member_count;
2115
2116 if (is_object)
2117 {
2118 m = cl->class_obj_members;
2119 member_count = cl->class_obj_member_count;
2120 }
2121 else
2122 {
2123 m = cl->class_class_members;
2124 member_count = cl->class_class_member_count;
2125 }
2126
2127 if (member_idx >= member_count)
2128 return NULL;
2129
2130 return m[member_idx].ocm_type;
2131}
2132
2133/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002134 * Handle ":enum" up to ":endenum".
2135 */
2136 void
2137ex_enum(exarg_T *eap UNUSED)
2138{
2139 // TODO
2140}
2141
2142/*
2143 * Handle ":type".
2144 */
2145 void
2146ex_type(exarg_T *eap UNUSED)
2147{
2148 // TODO
2149}
2150
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002151/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002152 * Returns OK if a member variable named "name" is present in the class "cl".
2153 * Otherwise returns FAIL. If found, the member variable typval is set in
2154 * "rettv". If "is_object" is TRUE, then the object member variable table is
2155 * searched. Otherwise the class member variable table is searched.
2156 */
2157 static int
2158get_member_tv(
2159 class_T *cl,
2160 int is_object,
2161 char_u *name,
2162 size_t namelen,
2163 typval_T *rettv)
2164{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002165 ocmember_T *m;
2166 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002167
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002168 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2169 &m_idx);
2170 if (m == NULL)
2171 return FAIL;
2172
2173 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002174 {
Ernie Rael64885642023-10-04 20:16:22 +02002175 semsg(_(e_cannot_access_private_variable_str), m->ocm_name,
2176 cl->class_name);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002177 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002178 }
2179
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002180 // The object only contains a pointer to the class, the member
2181 // values array follows right after that.
2182 object_T *obj = rettv->vval.v_object;
2183 if (is_object)
2184 {
2185 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2186 copy_tv(tv, rettv);
2187 }
2188 else
2189 copy_tv(&cl->class_members_tv[m_idx], rettv);
2190
2191 object_unref(obj);
2192
2193 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002194}
2195
2196/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002197 * Evaluate what comes after a class:
2198 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002199 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002200 * - class constructor: SomeClass.new()
2201 * - object member: someObject.varname
2202 * - object method: someObject.SomeMethod()
2203 *
2204 * "*arg" points to the '.'.
2205 * "*arg" is advanced to after the member name or method call.
2206 *
2207 * Returns FAIL or OK.
2208 */
2209 int
2210class_object_index(
2211 char_u **arg,
2212 typval_T *rettv,
2213 evalarg_T *evalarg,
2214 int verbose UNUSED) // give error messages
2215{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002216 if (VIM_ISWHITE((*arg)[1]))
2217 {
2218 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
2219 return FAIL;
2220 }
2221
2222 ++*arg;
2223 char_u *name = *arg;
2224 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2225 if (name_end == name)
2226 return FAIL;
2227 size_t len = name_end - name;
2228
Bram Moolenaar552bdca2023-02-17 21:08:50 +00002229 class_T *cl;
2230 if (rettv->v_type == VAR_CLASS)
2231 cl = rettv->vval.v_class;
2232 else // VAR_OBJECT
2233 {
2234 if (rettv->vval.v_object == NULL)
2235 {
2236 emsg(_(e_using_null_object));
2237 return FAIL;
2238 }
2239 cl = rettv->vval.v_object->obj_class;
2240 }
2241
Bram Moolenaard13dd302023-03-11 20:56:35 +00002242 if (cl == NULL)
2243 {
2244 emsg(_(e_incomplete_type));
2245 return FAIL;
2246 }
2247
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002248 if (*name_end == '(')
2249 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002250 ufunc_T *fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002251
Ernie Rael4d00b832023-09-11 19:54:42 +02002252 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002253 if (fp == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002254 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002255 method_not_found_msg(cl, rettv->v_type, name, len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002256 return FAIL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002257 }
2258
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002259 typval_T argvars[MAX_FUNC_ARGS + 1];
2260 int argcount = 0;
2261
2262 if (*fp->uf_name == '_')
2263 {
2264 // Cannot access a private method outside of a class
2265 semsg(_(e_cannot_access_private_method_str), name);
2266 return FAIL;
2267 }
2268
2269 char_u *argp = name_end;
2270 int ret = get_func_arguments(&argp, evalarg, 0,
2271 argvars, &argcount);
2272 if (ret == FAIL)
2273 return FAIL;
2274
2275 funcexe_T funcexe;
2276 CLEAR_FIELD(funcexe);
2277 funcexe.fe_evaluate = TRUE;
2278 if (rettv->v_type == VAR_OBJECT)
2279 {
2280 funcexe.fe_object = rettv->vval.v_object;
2281 ++funcexe.fe_object->obj_refcount;
2282 }
2283
2284 // Clear the class or object after calling the function, in
2285 // case the refcount is one.
2286 typval_T tv_tofree = *rettv;
2287 rettv->v_type = VAR_UNKNOWN;
2288
2289 // Call the user function. Result goes into rettv;
2290 int error = call_user_func_check(fp, argcount, argvars,
2291 rettv, &funcexe, NULL);
2292
2293 // Clear the previous rettv and the arguments.
2294 clear_tv(&tv_tofree);
2295 for (int idx = 0; idx < argcount; ++idx)
2296 clear_tv(&argvars[idx]);
2297
2298 if (error != FCERR_NONE)
2299 {
2300 user_func_error(error, printable_func_name(fp),
2301 funcexe.fe_found_var);
2302 return FAIL;
2303 }
2304 *arg = argp;
2305 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002306 }
2307
2308 else if (rettv->v_type == VAR_OBJECT)
2309 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002310 // Search in the object member variable table and the class member
2311 // variable table.
Yegappan Lakshmanan23c92d92023-09-09 11:33:29 +02002312 if (get_member_tv(cl, TRUE, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002313 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002314 *arg = name_end;
2315 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002316 }
2317
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002318 member_not_found_msg(cl, VAR_OBJECT, name, len);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002319 }
2320
Bram Moolenaard505d172022-12-18 21:42:55 +00002321 else if (rettv->v_type == VAR_CLASS)
2322 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002323 int m_idx;
2324
Bram Moolenaard505d172022-12-18 21:42:55 +00002325 // class member
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002326 ocmember_T *m = class_member_lookup(cl, name, len, &m_idx);
2327 if (m == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002328 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002329 member_not_found_msg(cl, VAR_CLASS, name, len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002330 return FAIL;
Bram Moolenaard505d172022-12-18 21:42:55 +00002331 }
2332
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002333 if (*name == '_')
2334 {
Ernie Rael64885642023-10-04 20:16:22 +02002335 semsg(_(e_cannot_access_private_variable_str), m->ocm_name,
2336 cl->class_name);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002337 return FAIL;
2338 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002339
2340 typval_T *tv = &cl->class_members_tv[m_idx];
2341 copy_tv(tv, rettv);
2342 class_unref(cl);
2343
2344 *arg = name_end;
2345 return OK;
Bram Moolenaard505d172022-12-18 21:42:55 +00002346 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002347
2348 return FAIL;
2349}
2350
2351/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002352 * If "arg" points to a class or object method, return it.
2353 * Otherwise return NULL.
2354 */
2355 ufunc_T *
2356find_class_func(char_u **arg)
2357{
2358 char_u *name = *arg;
2359 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2360 if (name_end == name || *name_end != '.')
2361 return NULL;
2362
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002363 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002364 size_t len = name_end - name;
2365 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002366 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00002367 if (eval_variable(name, (int)len,
2368 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002369 return NULL;
2370 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00002371 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002372
2373 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
2374 : tv.vval.v_object->obj_class;
2375 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002376 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002377 char_u *fname = name_end + 1;
2378 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
2379 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00002380 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002381 len = fname_end - fname;
2382
Ernie Rael4d00b832023-09-11 19:54:42 +02002383 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002384
Bram Moolenaareb533502022-12-14 15:06:11 +00002385fail_after_eval:
2386 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002387 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002388}
2389
2390/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002391 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002392 * Returns -1, if the variable is not found.
2393 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002394 */
2395 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002396class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002397{
Ernie Rael4d00b832023-09-11 19:54:42 +02002398 int idx;
2399 class_member_lookup(cl, name, namelen, &idx);
2400 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002401}
2402
2403/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002404 * Returns a pointer to the class member variable "name" in the class "cl".
2405 * Returns NULL if the variable is not found.
2406 * The member variable index is set in "idx".
2407 */
2408 ocmember_T *
2409class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2410{
Ernie Rael4d00b832023-09-11 19:54:42 +02002411 ocmember_T *ret_m = NULL;
2412 int ret_idx = -1;
2413 for (int i = 0; i < cl->class_class_member_count; ++i)
2414 {
2415 ocmember_T *m = &cl->class_class_members[i];
2416 if (namelen)
2417 {
2418 if (STRNCMP(name, m->ocm_name, namelen) == 0
2419 && m->ocm_name[namelen] == NUL)
2420 {
2421 ret_m = m;
2422 ret_idx = i;
2423 break;
2424 }
2425 }
2426 else if (STRCMP(name, m->ocm_name) == 0)
2427 {
2428 ret_m = m;
2429 ret_idx = i;
2430 break;
2431 }
2432 }
2433 if (idx != NULL)
2434 *idx = ret_idx;
2435 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002436}
2437
2438/*
2439 * Returns the index of class method "name" in the class "cl".
2440 * Returns -1, if the method is not found.
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002441 */
2442 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002443class_method_idx(class_T *cl, char_u *name, size_t namelen)
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002444{
Ernie Rael4d00b832023-09-11 19:54:42 +02002445 int idx;
2446 class_method_lookup(cl, name, namelen, &idx);
2447 return idx;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002448}
2449
2450/*
2451 * Returns a pointer to the class method "name" in class "cl".
2452 * Returns NULL if the method is not found.
2453 * The method index is set in "idx".
2454 */
2455 ufunc_T *
2456class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2457{
Ernie Rael4d00b832023-09-11 19:54:42 +02002458 ufunc_T *ret_fp = NULL;
2459 int ret_idx = -1;
2460 for (int i = 0; i < cl->class_class_function_count; ++i)
2461 {
2462 ufunc_T *fp = cl->class_class_functions[i];
2463 char_u *ufname = (char_u *)fp->uf_name;
2464 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2465 {
2466 ret_fp = fp;
2467 ret_idx = i;
2468 break;
2469 }
2470 }
2471 if (idx != NULL)
2472 *idx = ret_idx;
2473 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002474}
2475
2476/*
2477 * Returns the index of object member variable "name" in the class "cl".
2478 * Returns -1, if the variable is not found.
2479 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
2480 */
2481 int
2482object_member_idx(class_T *cl, char_u *name, size_t namelen)
2483{
Ernie Rael4d00b832023-09-11 19:54:42 +02002484 int idx;
2485 object_member_lookup(cl, name, namelen, &idx);
2486 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002487}
2488
2489/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002490 * Returns a pointer to the object member variable "name" in the class "cl".
2491 * Returns NULL if the variable is not found.
2492 * The object member variable index is set in "idx".
2493 */
2494 ocmember_T *
2495object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2496{
Ernie Rael4d00b832023-09-11 19:54:42 +02002497 ocmember_T *ret_m = NULL;
2498 int ret_idx = -1;
2499 for (int i = 0; i < cl->class_obj_member_count; ++i)
2500 {
2501 ocmember_T *m = &cl->class_obj_members[i];
2502 if (namelen)
2503 {
2504 if (STRNCMP(name, m->ocm_name, namelen) == 0
2505 && m->ocm_name[namelen] == NUL)
2506 {
2507 ret_m = m;
2508 ret_idx = i;
2509 break;
2510 }
2511 }
2512 else if (STRCMP(name, m->ocm_name) == 0)
2513 {
2514 ret_m = m;
2515 ret_idx = i;
2516 break;
2517 }
2518 }
2519 if (idx != NULL)
2520 *idx = ret_idx;
2521 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002522}
2523
2524/*
2525 * Returns the index of object method "name" in the class "cl".
2526 * Returns -1, if the method is not found.
2527 */
2528 int
2529object_method_idx(class_T *cl, char_u *name, size_t namelen)
2530{
Ernie Rael4d00b832023-09-11 19:54:42 +02002531 int idx;
2532 object_method_lookup(cl, name, namelen, &idx);
2533 return idx;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002534}
2535
2536/*
2537 * Returns a pointer to the object method "name" in class "cl".
2538 * Returns NULL if the method is not found.
2539 * The object method index is set in "idx".
2540 */
2541 ufunc_T *
2542object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2543{
Ernie Rael4d00b832023-09-11 19:54:42 +02002544 ufunc_T *ret_fp = NULL;
2545 int ret_idx = -1;
2546 for (int i = 0; i < cl->class_obj_method_count; ++i)
2547 {
2548 ufunc_T *fp = cl->class_obj_methods[i];
2549 // Use a separate pointer to avoid that ASAN complains about
2550 // uf_name[] only being 4 characters.
2551 char_u *ufname = (char_u *)fp->uf_name;
2552 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2553 {
2554 ret_fp = fp;
2555 ret_idx = i;
2556 break;
2557 }
2558 }
2559 if (idx != NULL)
2560 *idx = ret_idx;
2561 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002562}
2563
2564/*
2565 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
2566 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
2567 * object member variable.
2568 *
2569 * Returns a pointer to the member variable structure if variable is found.
2570 * Otherwise returns NULL. The member variable index is set in "*idx".
2571 */
2572 ocmember_T *
2573member_lookup(
2574 class_T *cl,
2575 vartype_T v_type,
2576 char_u *name,
2577 size_t namelen,
2578 int *idx)
2579{
2580 if (v_type == VAR_CLASS)
2581 return class_member_lookup(cl, name, namelen, idx);
2582 else
2583 return object_member_lookup(cl, name, namelen, idx);
2584}
2585
2586/*
2587 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
2588 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
2589 *
2590 * Returns a pointer to the method structure if variable is found.
2591 * Otherwise returns NULL. The method variable index is set in "*idx".
2592 */
2593 ufunc_T *
2594method_lookup(
2595 class_T *cl,
2596 vartype_T v_type,
2597 char_u *name,
2598 size_t namelen,
2599 int *idx)
2600{
2601 if (v_type == VAR_CLASS)
2602 return class_method_lookup(cl, name, namelen, idx);
2603 else
2604 return object_method_lookup(cl, name, namelen, idx);
2605}
2606
2607/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00002608 * Return TRUE if current context "cctx_arg" is inside class "cl".
2609 * Return FALSE if not.
2610 */
2611 int
2612inside_class(cctx_T *cctx_arg, class_T *cl)
2613{
2614 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002615 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002616 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002617 return TRUE;
2618 return FALSE;
2619}
2620
2621/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002622 * Make a copy of an object.
2623 */
2624 void
2625copy_object(typval_T *from, typval_T *to)
2626{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002627 if (from->vval.v_object == NULL)
2628 to->vval.v_object = NULL;
2629 else
2630 {
2631 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002632 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002633 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002634}
2635
2636/*
2637 * Free an object.
2638 */
2639 static void
2640object_clear(object_T *obj)
2641{
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002642 // Avoid a recursive call, it can happen if "obj" has a circular reference.
2643 obj->obj_refcount = INT_MAX;
2644
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002645 class_T *cl = obj->obj_class;
2646
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002647 if (!cl)
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002648 return;
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002649
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002650 // the member values are just after the object structure
2651 typval_T *tv = (typval_T *)(obj + 1);
2652 for (int i = 0; i < cl->class_obj_member_count; ++i)
2653 clear_tv(tv + i);
2654
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002655 // Remove from the list headed by "first_object".
2656 object_cleared(obj);
2657
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002658 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002659 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002660}
2661
2662/*
2663 * Unreference an object.
2664 */
2665 void
2666object_unref(object_T *obj)
2667{
2668 if (obj != NULL && --obj->obj_refcount <= 0)
2669 object_clear(obj);
2670}
2671
2672/*
2673 * Make a copy of a class.
2674 */
2675 void
2676copy_class(typval_T *from, typval_T *to)
2677{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002678 if (from->vval.v_class == NULL)
2679 to->vval.v_class = NULL;
2680 else
2681 {
2682 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002683 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002684 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002685}
2686
2687/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002688 * Free the class "cl" and its contents.
2689 */
2690 static void
2691class_free(class_T *cl)
2692{
2693 // Freeing what the class contains may recursively come back here.
2694 // Clear "class_name" first, if it is NULL the class does not need to
2695 // be freed.
2696 VIM_CLEAR(cl->class_name);
2697
2698 class_unref(cl->class_extends);
2699
2700 for (int i = 0; i < cl->class_interface_count; ++i)
2701 {
2702 vim_free(((char_u **)cl->class_interfaces)[i]);
2703 if (cl->class_interfaces_cl[i] != NULL)
2704 class_unref(cl->class_interfaces_cl[i]);
2705 }
2706 vim_free(cl->class_interfaces);
2707 vim_free(cl->class_interfaces_cl);
2708
2709 itf2class_T *next;
2710 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2711 {
2712 next = i2c->i2c_next;
2713 vim_free(i2c);
2714 }
2715
2716 for (int i = 0; i < cl->class_class_member_count; ++i)
2717 {
2718 ocmember_T *m = &cl->class_class_members[i];
2719 vim_free(m->ocm_name);
2720 vim_free(m->ocm_init);
2721 if (cl->class_members_tv != NULL)
2722 clear_tv(&cl->class_members_tv[i]);
2723 }
2724 vim_free(cl->class_class_members);
2725 vim_free(cl->class_members_tv);
2726
2727 for (int i = 0; i < cl->class_obj_member_count; ++i)
2728 {
2729 ocmember_T *m = &cl->class_obj_members[i];
2730 vim_free(m->ocm_name);
2731 vim_free(m->ocm_init);
2732 }
2733 vim_free(cl->class_obj_members);
2734
2735 for (int i = 0; i < cl->class_class_function_count; ++i)
2736 {
2737 ufunc_T *uf = cl->class_class_functions[i];
2738 func_clear_free(uf, FALSE);
2739 }
2740 vim_free(cl->class_class_functions);
2741
2742 for (int i = 0; i < cl->class_obj_method_count; ++i)
2743 {
2744 ufunc_T *uf = cl->class_obj_methods[i];
2745 func_clear_free(uf, FALSE);
2746 }
2747 vim_free(cl->class_obj_methods);
2748
2749 clear_type_list(&cl->class_type_list);
2750
2751 class_cleared(cl);
2752
2753 vim_free(cl);
2754}
2755
2756/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002757 * Unreference a class. Free it when the reference count goes down to zero.
2758 */
2759 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002760class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002761{
Bram Moolenaard505d172022-12-18 21:42:55 +00002762 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002763 class_free(cl);
2764}
2765
2766/*
2767 * Go through the list of all classes and free items without "copyID".
2768 */
2769 int
2770class_free_nonref(int copyID)
2771{
2772 int did_free = FALSE;
2773
2774 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002775 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002776 next_nonref_class = cl->class_next_used;
2777 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002778 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002779 // Free the class and items it contains.
2780 class_free(cl);
2781 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002782 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002783 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002784
2785 next_nonref_class = NULL;
2786 return did_free;
2787}
2788
2789 int
2790set_ref_in_classes(int copyID)
2791{
2792 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
2793 set_ref_in_item_class(cl, copyID, NULL, NULL);
2794
2795 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002796}
2797
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002798static object_T *first_object = NULL;
2799
2800/*
2801 * Call this function when an object has been created. It will be added to the
2802 * list headed by "first_object".
2803 */
2804 void
2805object_created(object_T *obj)
2806{
2807 if (first_object != NULL)
2808 {
2809 obj->obj_next_used = first_object;
2810 first_object->obj_prev_used = obj;
2811 }
2812 first_object = obj;
2813}
2814
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002815static object_T *next_nonref_obj = NULL;
2816
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002817/*
2818 * Call this function when an object has been cleared and is about to be freed.
2819 * It is removed from the list headed by "first_object".
2820 */
2821 void
2822object_cleared(object_T *obj)
2823{
2824 if (obj->obj_next_used != NULL)
2825 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
2826 if (obj->obj_prev_used != NULL)
2827 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
2828 else if (first_object == obj)
2829 first_object = obj->obj_next_used;
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002830
2831 // update the next object to check if needed
2832 if (obj == next_nonref_obj)
2833 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002834}
2835
2836/*
2837 * Go through the list of all objects and free items without "copyID".
2838 */
2839 int
2840object_free_nonref(int copyID)
2841{
2842 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002843
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002844 for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002845 {
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002846 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002847 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
2848 {
2849 // Free the object and items it contains.
2850 object_clear(obj);
2851 did_free = TRUE;
2852 }
2853 }
2854
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002855 next_nonref_obj = NULL;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002856 return did_free;
2857}
2858
LemonBoyafe04662023-08-23 21:08:11 +02002859/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002860 * Echo a class or object method not found message.
2861 */
2862 void
2863method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
2864{
2865 char_u *method_name = vim_strnsave(name, len);
2866 if ((v_type == VAR_OBJECT)
2867 && (class_method_idx(cl, name, len) >= 0))
2868 {
2869 // If this is a class method, then give a different error
2870 if (*name == '_')
2871 semsg(_(e_cannot_access_private_method_str), method_name);
2872 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002873 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002874 method_name, cl->class_name);
2875 }
2876 else if ((v_type == VAR_CLASS)
2877 && (object_method_idx(cl, name, len) >= 0))
2878 {
2879 // If this is an object method, then give a different error
2880 if (*name == '_')
2881 semsg(_(e_cannot_access_private_method_str), method_name);
2882 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002883 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002884 method_name, cl->class_name);
2885 }
2886 else
2887 semsg(_(e_method_not_found_on_class_str_str), cl->class_name,
2888 method_name);
2889 vim_free(method_name);
2890}
2891
2892/*
2893 * Echo a class or object member not found message.
2894 */
2895 void
2896member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
2897{
2898 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
2899
2900 if (v_type == VAR_OBJECT)
2901 {
2902 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02002903 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002904 varname, cl->class_name);
2905 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002906 semsg(_(e_variable_not_found_on_object_str_str), cl->class_name,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002907 varname);
2908 }
2909 else
2910 {
2911 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02002912 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002913 varname, cl->class_name);
2914 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002915 semsg(_(e_class_variable_str_not_found_in_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002916 varname, cl->class_name);
2917 }
2918 vim_free(varname);
2919}
2920
2921/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002922 * Return TRUE when the class "cl", its base class or one of the implemented
2923 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02002924 */
2925 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002926class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02002927{
2928 if (cl == other_cl)
2929 return TRUE;
2930
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002931 // Recursively check the base classes.
2932 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02002933 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002934 if (cl == other_cl)
2935 return TRUE;
2936 // Check the implemented interfaces and the super interfaces
2937 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002938 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002939 class_T *intf = cl->class_interfaces_cl[i];
2940 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002941 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002942 if (intf == other_cl)
2943 return TRUE;
2944 // check the super interfaces
2945 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002946 }
2947 }
LemonBoyafe04662023-08-23 21:08:11 +02002948 }
2949
2950 return FALSE;
2951}
2952
2953/*
2954 * "instanceof(object, classinfo)" function
2955 */
2956 void
2957f_instanceof(typval_T *argvars, typval_T *rettv)
2958{
2959 typval_T *object_tv = &argvars[0];
2960 typval_T *classinfo_tv = &argvars[1];
2961 listitem_T *li;
2962
2963 rettv->vval.v_number = VVAL_FALSE;
2964
2965 if (check_for_object_arg(argvars, 0) == FAIL
2966 || check_for_class_or_list_arg(argvars, 1) == FAIL)
2967 return;
2968
Ernie Rael3da696d2023-09-19 20:14:18 +02002969 if (object_tv->vval.v_object == NULL)
2970 return;
2971
LemonBoyafe04662023-08-23 21:08:11 +02002972 if (classinfo_tv->v_type == VAR_LIST)
2973 {
2974 FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
2975 {
2976 if (li->li_tv.v_type != VAR_CLASS)
2977 {
2978 emsg(_(e_class_required));
2979 return;
2980 }
2981
2982 if (class_instance_of(object_tv->vval.v_object->obj_class,
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002983 li->li_tv.vval.v_class) == TRUE)
LemonBoyafe04662023-08-23 21:08:11 +02002984 {
2985 rettv->vval.v_number = VVAL_TRUE;
2986 return;
2987 }
2988 }
2989 }
2990 else if (classinfo_tv->v_type == VAR_CLASS)
2991 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002992 rettv->vval.v_number = class_instance_of(object_tv->vval.v_object->obj_class,
2993 classinfo_tv->vval.v_class);
LemonBoyafe04662023-08-23 21:08:11 +02002994 }
2995}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002996
2997#endif // FEAT_EVAL