blob: 0cb353bb57f08dd7f4ac2b95ccae28dd824a8f92 [file] [log] [blame]
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * vim9class.c: Vim9 script class support
12 */
13
14#define USING_FLOAT_STUFF
15#include "vim.h"
16
17#if defined(FEAT_EVAL) || defined(PROTO)
18
19// When not generating protos this is included in proto.h
20#ifdef PROTO
21# include "vim9.h"
22#endif
23
Yegappan Lakshmanane651e112023-09-04 07:51:01 +020024static class_T *first_class = NULL;
25static class_T *next_nonref_class = NULL;
26
27/*
28 * Call this function when a class has been created. It will be added to the
29 * list headed by "first_class".
30 */
31 static void
32class_created(class_T *cl)
33{
34 if (first_class != NULL)
35 {
36 cl->class_next_used = first_class;
37 first_class->class_prev_used = cl;
38 }
39 first_class = cl;
40}
41
42/*
43 * Call this function when a class has been cleared and is about to be freed.
44 * It is removed from the list headed by "first_class".
45 */
46 static void
47class_cleared(class_T *cl)
48{
49 if (cl->class_next_used != NULL)
50 cl->class_next_used->class_prev_used = cl->class_prev_used;
51 if (cl->class_prev_used != NULL)
52 cl->class_prev_used->class_next_used = cl->class_next_used;
53 else if (first_class == cl)
54 first_class = cl->class_next_used;
55
56 // update the next class to check if needed
57 if (cl == next_nonref_class)
58 next_nonref_class = cl->class_next_used;
59}
60
Bram Moolenaarc1c365c2022-12-04 20:13:24 +000061/*
Bram Moolenaard505d172022-12-18 21:42:55 +000062 * Parse a member declaration, both object and class member.
63 * Returns OK or FAIL. When OK then "varname_end" is set to just after the
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +020064 * variable name and "type_ret" is set to the declared or detected type.
Bram Moolenaard505d172022-12-18 21:42:55 +000065 * "init_expr" is set to the initialisation expression (allocated), if there is
Bram Moolenaar554d0312023-01-05 19:59:18 +000066 * one. For an interface "init_expr" is NULL.
Bram Moolenaard505d172022-12-18 21:42:55 +000067 */
68 static int
69parse_member(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020070 exarg_T *eap,
71 char_u *line,
72 char_u *varname,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020073 int has_public, // TRUE if "public" seen before "varname"
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020074 char_u **varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +020075 int *has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020076 garray_T *type_list,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020077 type_T **type_ret,
78 char_u **init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +000079{
80 *varname_end = to_name_end(varname, FALSE);
81 if (*varname == '_' && has_public)
82 {
RestorerZ7fe8f432023-09-24 23:21:24 +020083 semsg(_(e_public_variable_name_cannot_start_with_underscore_str), line);
Bram Moolenaard505d172022-12-18 21:42:55 +000084 return FAIL;
85 }
86
87 char_u *colon = skipwhite(*varname_end);
88 char_u *type_arg = colon;
89 type_T *type = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +020090 *has_type = FALSE;
Bram Moolenaard505d172022-12-18 21:42:55 +000091 if (*colon == ':')
92 {
93 if (VIM_ISWHITE(**varname_end))
94 {
95 semsg(_(e_no_white_space_allowed_before_colon_str), varname);
96 return FAIL;
97 }
98 if (!VIM_ISWHITE(colon[1]))
99 {
100 semsg(_(e_white_space_required_after_str_str), ":", varname);
101 return FAIL;
102 }
103 type_arg = skipwhite(colon + 1);
104 type = parse_type(&type_arg, type_list, TRUE);
105 if (type == NULL)
106 return FAIL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200107 *has_type = TRUE;
Bram Moolenaard505d172022-12-18 21:42:55 +0000108 }
109
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200110 char_u *init_arg = skipwhite(type_arg);
111 if (type == NULL && *init_arg != '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000112 {
113 emsg(_(e_type_or_initialization_required));
114 return FAIL;
115 }
116
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200117 if (init_expr == NULL && *init_arg == '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000118 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200119 emsg(_(e_cannot_initialize_variable_in_interface));
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200120 return FAIL;
121 }
122
123 if (*init_arg == '=')
124 {
125 evalarg_T evalarg;
126 char_u *expr_start, *expr_end;
127
128 if (!VIM_ISWHITE(init_arg[-1]) || !VIM_ISWHITE(init_arg[1]))
Bram Moolenaard505d172022-12-18 21:42:55 +0000129 {
130 semsg(_(e_white_space_required_before_and_after_str_at_str),
131 "=", type_arg);
132 return FAIL;
133 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200134 init_arg = skipwhite(init_arg + 1);
Bram Moolenaard505d172022-12-18 21:42:55 +0000135
Bram Moolenaard505d172022-12-18 21:42:55 +0000136 fill_evalarg_from_eap(&evalarg, eap, FALSE);
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200137 (void)skip_expr_concatenate(&init_arg, &expr_start, &expr_end, &evalarg);
Bram Moolenaard505d172022-12-18 21:42:55 +0000138
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +0200139 // No type specified for the member. Set it to "any" and the correct
140 // type will be set when the object is instantiated.
Bram Moolenaard505d172022-12-18 21:42:55 +0000141 if (type == NULL)
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200142 type = &t_any;
Bram Moolenaard505d172022-12-18 21:42:55 +0000143
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200144 *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
145 // Free the memory pointed by expr_start.
Bram Moolenaard505d172022-12-18 21:42:55 +0000146 clear_evalarg(&evalarg, NULL);
147 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200148 else if (!valid_declaration_type(type))
Bram Moolenaard505d172022-12-18 21:42:55 +0000149 return FAIL;
150
151 *type_ret = type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000152 return OK;
153}
154
155/*
156 * Add a member to an object or a class.
157 * Returns OK when successful, "init_expr" will be consumed then.
158 * Returns FAIL otherwise, caller might need to free "init_expr".
159 */
160 static int
161add_member(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200162 garray_T *gap,
163 char_u *varname,
164 char_u *varname_end,
165 int has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200166 int has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200167 type_T *type,
168 char_u *init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +0000169{
170 if (ga_grow(gap, 1) == FAIL)
171 return FAIL;
172 ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
173 m->ocm_name = vim_strnsave(varname, varname_end - varname);
=?UTF-8?q?Ola=20S=C3=B6der?=d8742472023-03-05 13:12:32 +0000174 m->ocm_access = has_public ? VIM_ACCESS_ALL
175 : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200176 m->ocm_has_type = has_type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000177 m->ocm_type = type;
178 if (init_expr != NULL)
179 m->ocm_init = init_expr;
180 ++gap->ga_len;
181 return OK;
182}
183
184/*
185 * Move the class or object members found while parsing a class into the class.
186 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000187 * "parent_members" points to the members in the parent class (if any)
188 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000189 * "members" will be set to the newly allocated array of members and
190 * "member_count" set to the number of members.
191 * Returns OK or FAIL.
192 */
193 static int
194add_members_to_class(
195 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000196 ocmember_T *parent_members,
197 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000198 ocmember_T **members,
199 int *member_count)
200{
Bram Moolenaar83677162023-01-08 19:54:10 +0000201 *member_count = parent_count + gap->ga_len;
202 *members = *member_count == 0 ? NULL
203 : ALLOC_MULT(ocmember_T, *member_count);
204 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000205 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000206 for (int i = 0; i < parent_count; ++i)
207 {
208 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000209 ocmember_T *m = *members + i;
210 *m = parent_members[i];
211 m->ocm_name = vim_strsave(m->ocm_name);
212 if (m->ocm_init != NULL)
213 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000214 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000215 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000216 // new members are moved
217 mch_memmove(*members + parent_count,
218 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000219 VIM_CLEAR(gap->ga_data);
220 return OK;
221}
222
223/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000224 * Convert a member index "idx" of interface "itf" to the member index of class
225 * "cl" implementing that interface.
226 */
227 int
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200228object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000229{
Ernie Rael18143d32023-09-04 22:30:41 +0200230 if (idx >= (is_method ? itf->class_obj_method_count
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200231 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000232 {
233 siemsg("index %d out of range for interface %s", idx, itf->class_name);
234 return 0;
235 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200236
237 // If "cl" is the interface or the class that is extended, then the method
238 // index can be used directly and there is no need to search for the method
239 // index in one of the child classes.
240 if (cl == itf)
241 return idx;
242
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200243 itf2class_T *i2c = NULL;
244 int searching = TRUE;
245 int method_offset = 0;
246
Ernie Raelcf138d42023-09-06 20:45:03 +0200247 for (class_T *super = cl; super != NULL && searching;
248 super = super->class_extends)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200249 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200250 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200251 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200252 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
253 {
254 searching = FALSE;
255 break;
256 }
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200257 }
258 if (searching && is_method)
259 // The parent class methods are stored after the current class
260 // methods.
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200261 method_offset += super->class_obj_method_count_child;
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200262 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000263 if (i2c == NULL)
264 {
265 siemsg("class %s not found on interface %s",
266 cl->class_name, itf->class_name);
267 return 0;
268 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +0200269
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200270 // A table follows the i2c for the class
271 int *table = (int *)(i2c + 1);
272 // "method_offset" is 0, if method is in the current class. If method
273 // is in a parent class, then it is non-zero.
274 return table[idx] + method_offset;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000275}
276
277/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200278 * Check whether a class named "extends_name" is present. If the class is
279 * valid, then "extends_clp" is set with the class pointer.
280 * Returns TRUE if the class name "extends_names" is a valid class.
281 */
282 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200283validate_extends_class(
284 char_u *extends_name,
285 class_T **extends_clp,
286 int is_class)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200287{
288 typval_T tv;
289 int success = FALSE;
290
291 tv.v_type = VAR_UNKNOWN;
292 if (eval_variable_import(extends_name, &tv) == FAIL)
293 {
294 semsg(_(e_class_name_not_found_str), extends_name);
295 return success;
296 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200297
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200298 if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
299 || (is_class
300 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
301 || (!is_class
302 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0))
303 // a interface cannot extend a class and a class cannot extend an
304 // interface.
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200305 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200306 else
307 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200308 class_T *extends_cl = tv.vval.v_class;
309 ++extends_cl->class_refcount;
310 *extends_clp = extends_cl;
311 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200312 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200313 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200314
315 return success;
316}
317
318/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200319 * Check method names in the parent class lineage to make sure the access is
320 * the same for overridden methods.
321 */
322 static int
323validate_extends_methods(
324 garray_T *objmethods_gap,
325 class_T *extends_cl)
326{
327 class_T *super = extends_cl;
328 int method_count = objmethods_gap->ga_len;
329 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
330
331 while (super != NULL)
332 {
333 int extends_method_count = super->class_obj_method_count_child;
334 if (extends_method_count == 0)
335 {
336 super = super->class_extends;
337 continue;
338 }
339
340 ufunc_T **extends_methods = super->class_obj_methods;
341
342 for (int i = 0; i < extends_method_count; i++)
343 {
344 char_u *pstr = extends_methods[i]->uf_name;
345 int extends_private = (*pstr == '_');
346 if (extends_private)
347 pstr++;
348
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200349 // When comparing the method names, ignore the access type (public
350 // and private methods are considered the same).
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200351 for (int j = 0; j < method_count; j++)
352 {
353 char_u *qstr = cl_fp[j]->uf_name;
354 int priv_method = (*qstr == '_');
355 if (priv_method)
356 qstr++;
357 if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
358 {
359 // Method access is different between the super class and
360 // the subclass
361 semsg(_(e_method_str_of_class_str_has_different_access),
362 cl_fp[j]->uf_name, super->class_name);
363 return FALSE;
364 }
365 }
366 }
367 super = super->class_extends;
368 }
369
370 return TRUE;
371}
372
373/*
374 * Check whether a object member variable in "objmembers_gap" is a duplicate of
375 * a member in any of the extended parent class lineage. Returns TRUE if there
376 * are no duplicates.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200377 */
378 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200379extends_check_dup_members(
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200380 garray_T *objmembers_gap,
381 class_T *extends_cl)
382{
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200383 int member_count = objmembers_gap->ga_len;
384 if (member_count == 0)
385 return TRUE;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200386
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200387 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
388
389 // Validate each member variable
390 for (int c_i = 0; c_i < member_count; c_i++)
391 {
392 class_T *p_cl = extends_cl;
393 ocmember_T *c_m = members + c_i;
394 char_u *pstr = (*c_m->ocm_name == '_')
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200395 ? c_m->ocm_name + 1 : c_m->ocm_name;
396
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200397 // Check in all the parent classes in the lineage
398 while (p_cl != NULL)
399 {
400 int p_member_count = p_cl->class_obj_member_count;
401 if (p_member_count == 0)
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200402 {
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200403 p_cl = p_cl->class_extends;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200404 continue;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200405 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200406 ocmember_T *p_members = p_cl->class_obj_members;
407
408 // Compare against all the members in the parent class
409 for (int p_i = 0; p_i < p_member_count; p_i++)
410 {
411 ocmember_T *p_m = p_members + p_i;
412 char_u *qstr = (*p_m->ocm_name == '_')
413 ? p_m->ocm_name + 1 : p_m->ocm_name;
414 if (STRCMP(pstr, qstr) == 0)
415 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200416 semsg(_(e_duplicate_variable_str), c_m->ocm_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200417 return FALSE;
418 }
419 }
420
421 p_cl = p_cl->class_extends;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200422 }
423 }
424
425 return TRUE;
426}
427
428/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200429 * Compare the variable type of interface variables in "objmembers_gap" against
430 * the variable in any of the extended super interface lineage. Used to
431 * compare the variable types when extending interfaces. Returns TRUE if the
432 * variable types are the same.
433 */
434 static int
435extends_check_intf_var_type(
436 garray_T *objmembers_gap,
437 class_T *extends_cl)
438{
439 int member_count = objmembers_gap->ga_len;
440 if (member_count == 0)
441 return TRUE;
442
443 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
444
445 // Validate each member variable
446 for (int c_i = 0; c_i < member_count; c_i++)
447 {
448 class_T *p_cl = extends_cl;
449 ocmember_T *c_m = members + c_i;
450 int var_found = FALSE;
451
452 // Check in all the parent classes in the lineage
453 while (p_cl != NULL && !var_found)
454 {
455 int p_member_count = p_cl->class_obj_member_count;
456 if (p_member_count == 0)
457 {
458 p_cl = p_cl->class_extends;
459 continue;
460 }
461 ocmember_T *p_members = p_cl->class_obj_members;
462
463 // Compare against all the members in the parent class
464 for (int p_i = 0; p_i < p_member_count; p_i++)
465 {
466 where_T where = WHERE_INIT;
467 ocmember_T *p_m = p_members + p_i;
468
469 if (STRCMP(p_m->ocm_name, c_m->ocm_name) != 0)
470 continue;
471
472 // Ensure the type is matching.
473 where.wt_func_name = (char *)c_m->ocm_name;
474 where.wt_kind = WT_MEMBER;
475
476 if (check_type(p_m->ocm_type, c_m->ocm_type, TRUE,
477 where) == FAIL)
478 return FALSE;
479
480 var_found = TRUE;
481 }
482
483 p_cl = p_cl->class_extends;
484 }
485 }
486
487 return TRUE;
488}
489
490/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200491 * When extending an abstract class, check whether all the abstract methods in
492 * the parent class are implemented. Returns TRUE if all the methods are
493 * implemented.
494 */
495 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200496validate_abstract_class_methods(
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200497 garray_T *classmethods_gap,
498 garray_T *objmethods_gap,
499 class_T *extends_cl)
500{
501 for (int loop = 1; loop <= 2; ++loop)
502 {
503 // loop == 1: check class methods
504 // loop == 2: check object methods
505 int extends_method_count = loop == 1
506 ? extends_cl->class_class_function_count
507 : extends_cl->class_obj_method_count;
508 if (extends_method_count == 0)
509 continue;
510
511 ufunc_T **extends_methods = loop == 1
512 ? extends_cl->class_class_functions
513 : extends_cl->class_obj_methods;
514
515 int method_count = loop == 1 ? classmethods_gap->ga_len
516 : objmethods_gap->ga_len;
517 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
518 ? classmethods_gap->ga_data
519 : objmethods_gap->ga_data);
520
521 for (int i = 0; i < extends_method_count; i++)
522 {
523 ufunc_T *uf = extends_methods[i];
Yegappan Lakshmanan1db15142023-09-19 20:34:05 +0200524 if (!IS_ABSTRACT_METHOD(uf))
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200525 continue;
526
527 int method_found = FALSE;
528
529 for (int j = 0; j < method_count; j++)
530 {
531 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
532 {
533 method_found = TRUE;
534 break;
535 }
536 }
537
538 if (!method_found)
539 {
540 semsg(_(e_abstract_method_str_not_found), uf->uf_name);
541 return FALSE;
542 }
543 }
544 }
545
546 return TRUE;
547}
548
549/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200550 * Returns TRUE if the interface variable "if_var" is present in the list of
551 * variables in "cl_mt" or in the parent lineage of one of the extended classes
552 * in "extends_cl". For a class variable, 'is_class_var' is TRUE.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200553 */
554 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200555intf_variable_present(
556 char_u *intf_class_name,
557 ocmember_T *if_var,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200558 ocmember_T *cl_mt,
559 int cl_member_count,
560 class_T *extends_cl)
561{
562 int variable_present = FALSE;
563
564 for (int cl_i = 0; cl_i < cl_member_count; ++cl_i)
565 {
566 ocmember_T *m = &cl_mt[cl_i];
567 where_T where = WHERE_INIT;
568
569 if (STRCMP(if_var->ocm_name, m->ocm_name) != 0)
570 continue;
571
572 // Ensure the access type is same
573 if (if_var->ocm_access != m->ocm_access)
574 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200575 semsg(_(e_variable_str_of_interface_str_has_different_access),
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200576 if_var->ocm_name, intf_class_name);
577 return FALSE;
578 }
579
580 // Ensure the type is matching.
581 if (m->ocm_type == &t_any)
582 {
583 // variable type is not specified. Use the variable type in the
584 // interface.
585 m->ocm_type = if_var->ocm_type;
586 }
587 else
588 {
589 where.wt_func_name = (char *)m->ocm_name;
590 where.wt_kind = WT_MEMBER;
591 if (check_type(if_var->ocm_type, m->ocm_type, TRUE,
592 where) == FAIL)
593 return FALSE;
594 }
595
596 variable_present = TRUE;
597 break;
598 }
599
600 if (!variable_present && extends_cl != NULL)
601 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200602 int ext_cl_count = extends_cl->class_obj_member_count;
603 ocmember_T *ext_cl_mt = extends_cl->class_obj_members;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200604 return intf_variable_present(intf_class_name, if_var,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200605 ext_cl_mt, ext_cl_count,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200606 extends_cl->class_extends);
607 }
608
609 return variable_present;
610}
611
612/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200613 * Check the variables of the interface class "ifcl" match object variables
614 * ("objmembers_gap") of a class.
615 * Returns TRUE if the object variables names are valid.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200616 */
617 static int
618validate_interface_variables(
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200619 char_u *intf_class_name,
620 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200621 garray_T *objmembers_gap,
622 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200623{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200624 int if_count = ifcl->class_obj_member_count;
625 if (if_count == 0)
626 return TRUE;
627
628 ocmember_T *if_ms = ifcl->class_obj_members;
629 ocmember_T *cl_ms = (ocmember_T *)(objmembers_gap->ga_data);
630 int cl_count = objmembers_gap->ga_len;
631 for (int if_i = 0; if_i < if_count; ++if_i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200632 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200633 if (!intf_variable_present(intf_class_name, &if_ms[if_i], cl_ms,
634 cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200635 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200636 semsg(_(e_variable_str_of_interface_str_not_implemented),
637 if_ms[if_i].ocm_name, intf_class_name);
638 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200639 }
640 }
641
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200642 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200643}
644
645/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200646 * Returns TRUE if the method signature of "if_method" and "cl_method" matches.
647 */
648 static int
649intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method)
650{
651 where_T where = WHERE_INIT;
652
653 // Ensure the type is matching.
654 where.wt_func_name = (char *)if_method->uf_name;
655 where.wt_kind = WT_METHOD;
656 if (check_type(if_method->uf_func_type, cl_method->uf_func_type, TRUE,
657 where) == FAIL)
658 return FALSE;
659
660 return TRUE;
661}
662
663/*
664 * Returns TRUE if the interface method "if_ufunc" is present in the list of
665 * methods in "cl_fp" or in the parent lineage of one of the extended classes
666 * in "extends_cl". For a class method, 'is_class_method' is TRUE.
667 */
668 static int
669intf_method_present(
670 ufunc_T *if_ufunc,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200671 ufunc_T **cl_fp,
672 int cl_count,
673 class_T *extends_cl)
674{
675 int method_present = FALSE;
676
677 for (int cl_i = 0; cl_i < cl_count; ++cl_i)
678 {
679 char_u *cl_name = cl_fp[cl_i]->uf_name;
680 if (STRCMP(if_ufunc->uf_name, cl_name) == 0)
681 {
682 // Ensure the type is matching.
683 if (!intf_method_type_matches(if_ufunc, cl_fp[cl_i]))
684 return FALSE;
685 method_present = TRUE;
686 break;
687 }
688 }
689
690 if (!method_present && extends_cl != NULL)
691 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200692 ufunc_T **ext_cl_fp = (ufunc_T **)(extends_cl->class_obj_methods);
693 int ext_cl_count = extends_cl->class_obj_method_count;
694 return intf_method_present(if_ufunc, ext_cl_fp, ext_cl_count,
695 extends_cl->class_extends);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200696 }
697
698 return method_present;
699}
700
701/*
702 * Validate that a new class implements all the class/instance methods in the
703 * interface "ifcl". The new class methods are in "classfunctions_gap" and the
704 * new object methods are in "objmemthods_gap". Also validates the method
705 * types.
706 * Returns TRUE if all the interface class/object methods are implemented in
707 * the new class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200708 */
709 static int
710validate_interface_methods(
711 char_u *intf_class_name,
712 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200713 garray_T *objmethods_gap,
714 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200715{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200716 int if_count = ifcl->class_obj_method_count;
717 if (if_count == 0)
718 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200719
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200720 ufunc_T **if_fp = ifcl->class_obj_methods;
721 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
722 int cl_count = objmethods_gap->ga_len;
723 for (int if_i = 0; if_i < if_count; ++if_i)
724 {
725 char_u *if_name = if_fp[if_i]->uf_name;
726
727 if (!intf_method_present(if_fp[if_i], cl_fp, cl_count, extends_cl))
728 {
729 semsg(_(e_method_str_of_interface_str_not_implemented),
730 if_name, intf_class_name);
731 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200732 }
733 }
734
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200735 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200736}
737
738/*
739 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200740 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200741 * object methods and object members in the new class are in
742 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
743 * "objmembers_gap" respectively.
744 */
745 static int
746validate_implements_classes(
747 garray_T *impl_gap,
748 class_T **intf_classes,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200749 garray_T *objmethods_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200750 garray_T *objmembers_gap,
751 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200752{
753 int success = TRUE;
754
755 for (int i = 0; i < impl_gap->ga_len && success; ++i)
756 {
757 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
758 typval_T tv;
759 tv.v_type = VAR_UNKNOWN;
760 if (eval_variable_import(impl, &tv) == FAIL)
761 {
762 semsg(_(e_interface_name_not_found_str), impl);
763 success = FALSE;
764 break;
765 }
766
767 if (tv.v_type != VAR_CLASS
768 || tv.vval.v_class == NULL
769 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
770 {
771 semsg(_(e_not_valid_interface_str), impl);
772 success = FALSE;
773 clear_tv(&tv);
774 break;
775 }
776
777 class_T *ifcl = tv.vval.v_class;
778 intf_classes[i] = ifcl;
779 ++ifcl->class_refcount;
780
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200781 // check the variables of the interface match the members of the class
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200782 success = validate_interface_variables(impl, ifcl, objmembers_gap,
783 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200784
785 // check the functions/methods of the interface match the
786 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200787 if (success)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200788 success = validate_interface_methods(impl, ifcl, objmethods_gap,
789 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200790 clear_tv(&tv);
791 }
792
793 return success;
794}
795
796/*
797 * Check no function argument name is used as a class member.
798 * (Object members are always accessed with "this." prefix, so no need
799 * to check them.)
800 */
801 static int
802check_func_arg_names(
803 garray_T *classfunctions_gap,
804 garray_T *objmethods_gap,
805 garray_T *classmembers_gap)
806{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200807 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200808 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200809 {
810 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
811
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200812 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200813 {
814 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
815
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200816 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200817 {
818 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
819 garray_T *mgap = classmembers_gap;
820
821 // Check all the class member names
822 for (int mi = 0; mi < mgap->ga_len; ++mi)
823 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200824 char_u *mname =
825 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200826 if (STRCMP(aname, mname) == 0)
827 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200828 if (uf->uf_script_ctx.sc_sid > 0)
829 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
830
831 semsg(_(e_argument_already_declared_in_class_str),
832 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200833
834 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200835 }
836 }
837 }
838 }
839 }
840
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200841 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200842}
843
844/*
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200845 * Returns TRUE if 'varname' is a reserved keyword name
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200846 */
847 static int
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200848is_reserved_varname(char_u *varname, char_u *varname_end)
849{
850 int reserved = FALSE;
851 char_u save_varname_end = *varname_end;
852 *varname_end = NUL;
853
854 reserved = check_reserved_name(varname, FALSE) == FAIL;
855
856 *varname_end = save_varname_end;
857
858 return reserved;
859}
860
861/*
862 * Returns TRUE if the variable "varname" is already defined either as a class
863 * variable or as an object variable.
864 */
865 static int
866is_duplicate_variable(
867 garray_T *class_members,
868 garray_T *obj_members,
869 char_u *varname,
870 char_u *varname_end)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200871{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200872 char_u *name = vim_strnsave(varname, varname_end - varname);
873 char_u *pstr = (*name == '_') ? name + 1 : name;
874 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200875
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200876 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200877 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200878 // loop == 1: class variables, loop == 2: object variables
879 garray_T *vgap = (loop == 1) ? class_members : obj_members;
880 for (int i = 0; i < vgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200881 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200882 ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i;
883 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1
884 : m->ocm_name;
885 if (STRCMP(pstr, qstr) == 0)
886 {
887 semsg(_(e_duplicate_variable_str), name);
888 dup = TRUE;
889 break;
890 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200891 }
892 }
893
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200894 vim_free(name);
895 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200896}
897
898/*
899 * Returns TRUE if the method "name" is already defined.
900 */
901 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200902is_duplicate_method(
903 garray_T *classmethods_gap,
904 garray_T *objmethods_gap,
905 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200906{
907 char_u *pstr = (*name == '_') ? name + 1 : name;
908
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200909 // loop 1: class methods, loop 2: object methods
910 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200911 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200912 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
913 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200914 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200915 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
916 char_u *qstr = *n == '_' ? n + 1 : n;
917 if (STRCMP(pstr, qstr) == 0)
918 {
919 semsg(_(e_duplicate_function_str), name);
920 return TRUE;
921 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200922 }
923 }
924
925 return FALSE;
926}
927
928/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200929 * Returns TRUE if the constructor is valid.
930 */
931 static int
932is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
933{
934 // Constructors are not allowed in abstract classes.
935 if (is_abstract)
936 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200937 emsg(_(e_cannot_define_new_method_in_abstract_class));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200938 return FALSE;
939 }
940 // A constructor is always static, no need to define it so.
941 if (has_static)
942 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200943 emsg(_(e_cannot_define_new_method_as_static));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200944 return FALSE;
945 }
946 // A return type should not be specified for the new()
947 // constructor method.
948 if (uf->uf_ret_type->tt_type != VAR_VOID)
949 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200950 emsg(_(e_cannot_use_a_return_type_with_new_method));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200951 return FALSE;
952 }
953 return TRUE;
954}
955
956/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200957 * Update the interface class lookup table for the member index on the
958 * interface to the member index in the class implementing the interface.
959 * And a lookup table for the object method index on the interface
960 * to the object method index in the class implementing the interface.
961 * This is also used for updating the lookup table for the extended class
962 * hierarchy.
963 */
964 static int
965update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200966 class_T *ifcl,
967 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200968 garray_T *objmethods,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200969 int pobj_method_offset)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200970{
971 if (ifcl == NULL)
972 return OK;
973
974 // Table for members.
975 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200976 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200977 if (if2cl == NULL)
978 return FAIL;
979 if2cl->i2c_next = ifcl->class_itf2class;
980 ifcl->class_itf2class = if2cl;
981 if2cl->i2c_class = cl;
982 if2cl->i2c_is_method = FALSE;
983
984 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
985 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
986 {
987 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200988 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200989 {
990 int *table = (int *)(if2cl + 1);
991 table[if_i] = cl_i;
992 break;
993 }
994 }
995
996 // Table for methods.
997 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200998 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200999 if (if2cl == NULL)
1000 return FAIL;
1001 if2cl->i2c_next = ifcl->class_itf2class;
1002 ifcl->class_itf2class = if2cl;
1003 if2cl->i2c_class = cl;
1004 if2cl->i2c_is_method = TRUE;
1005
1006 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
1007 {
1008 int done = FALSE;
1009 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
1010 {
1011 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001012 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001013 {
1014 int *table = (int *)(if2cl + 1);
1015 table[if_i] = cl_i;
1016 done = TRUE;
1017 break;
1018 }
1019 }
1020
1021 // extended class object method is not overridden by the child class.
1022 // Keep the method declared in one of the parent classes in the
1023 // lineage.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001024 if (!done)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001025 {
1026 // If "ifcl" is not the immediate parent of "cl", then search in
1027 // the intermediate parent classes.
1028 if (cl->class_extends != ifcl)
1029 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001030 class_T *parent = cl->class_extends;
1031 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001032
1033 while (!done && parent != NULL && parent != ifcl)
1034 {
1035
1036 for (int cl_i = 0;
1037 cl_i < parent->class_obj_method_count_child; ++cl_i)
1038 {
1039 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
1040 parent->class_obj_methods[cl_i]->uf_name)
1041 == 0)
1042 {
1043 int *table = (int *)(if2cl + 1);
1044 table[if_i] = method_offset + cl_i;
1045 done = TRUE;
1046 break;
1047 }
1048 }
1049 method_offset += parent->class_obj_method_count_child;
1050 parent = parent->class_extends;
1051 }
1052 }
1053
1054 if (!done)
1055 {
1056 int *table = (int *)(if2cl + 1);
1057 table[if_i] = pobj_method_offset + if_i;
1058 }
1059 }
1060 }
1061
1062 return OK;
1063}
1064
1065/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001066 * Update the member and object method lookup tables for a new class in the
1067 * interface class.
1068 * For each interface add a lookup table for the member index on the interface
1069 * to the member index in the new class. And a lookup table for the object
1070 * method index on the interface to the object method index in the new class.
1071 */
1072 static int
1073add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
1074{
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001075 // update the lookup table for all the implemented interfaces
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001076 for (int i = 0; i < cl->class_interface_count; ++i)
1077 {
1078 class_T *ifcl = cl->class_interfaces_cl[i];
1079
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001080 // update the lookup table for this interface and all its super
1081 // interfaces.
1082 while (ifcl != NULL)
1083 {
1084 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
1085 0) == FAIL)
1086 return FAIL;
1087 ifcl = ifcl->class_extends;
1088 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001089 }
1090
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001091 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001092 if (extends_cl != NULL)
1093 {
1094 class_T *pclass = extends_cl;
1095 int pobj_method_offset = objmethods_gap->ga_len;
1096
1097 // Update the entire lineage of extended classes.
1098 while (pclass != NULL)
1099 {
1100 if (update_member_method_lookup_table(pclass, cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001101 objmethods_gap, pobj_method_offset) == FAIL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001102 return FAIL;
1103
1104 pobj_method_offset += pclass->class_obj_method_count_child;
1105 pclass = pclass->class_extends;
1106 }
1107 }
1108
1109 return OK;
1110}
1111
1112/*
1113 * Add class members to a new class. Allocate a typval for each class member
1114 * and initialize it.
1115 */
1116 static void
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001117add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001118{
1119 // Allocate a typval for each class member and initialize it.
1120 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
1121 cl->class_class_member_count);
1122 if (cl->class_members_tv == NULL)
1123 return;
1124
1125 for (int i = 0; i < cl->class_class_member_count; ++i)
1126 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001127 ocmember_T *m = &cl->class_class_members[i];
1128 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001129 if (m->ocm_init != NULL)
1130 {
1131 typval_T *etv = eval_expr(m->ocm_init, eap);
1132 if (etv != NULL)
1133 {
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001134 if (m->ocm_type->tt_type == VAR_ANY
1135 && !m->ocm_has_type
1136 && etv->v_type != VAR_SPECIAL)
1137 // If the member variable type is not yet set, then use
1138 // the initialization expression type.
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001139 m->ocm_type = typval2type(etv, get_copyID(),
1140 type_list_gap,
1141 TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001142 *tv = *etv;
1143 vim_free(etv);
1144 }
1145 }
1146 else
1147 {
1148 // TODO: proper default value
1149 tv->v_type = m->ocm_type->tt_type;
1150 tv->vval.v_string = NULL;
1151 }
1152 }
1153}
1154
1155/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001156 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001157 */
1158 static void
1159add_default_constructor(
1160 class_T *cl,
1161 garray_T *classfunctions_gap,
1162 garray_T *type_list_gap)
1163{
1164 garray_T fga;
1165
1166 ga_init2(&fga, 1, 1000);
1167 ga_concat(&fga, (char_u *)"new(");
1168 for (int i = 0; i < cl->class_obj_member_count; ++i)
1169 {
1170 if (i > 0)
1171 ga_concat(&fga, (char_u *)", ");
1172 ga_concat(&fga, (char_u *)"this.");
1173 ocmember_T *m = cl->class_obj_members + i;
1174 ga_concat(&fga, (char_u *)m->ocm_name);
1175 ga_concat(&fga, (char_u *)" = v:none");
1176 }
1177 ga_concat(&fga, (char_u *)")\nenddef\n");
1178 ga_append(&fga, NUL);
1179
1180 exarg_T fea;
1181 CLEAR_FIELD(fea);
1182 fea.cmdidx = CMD_def;
1183 fea.cmd = fea.arg = fga.ga_data;
1184
1185 garray_T lines_to_free;
1186 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1187
h-eastb895b0f2023-09-24 15:46:31 +02001188 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
1189 cl->class_obj_members, cl->class_obj_member_count);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001190
1191 ga_clear_strings(&lines_to_free);
1192 vim_free(fga.ga_data);
1193
1194 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1195 {
1196 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001197 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001198 ++classfunctions_gap->ga_len;
1199
1200 nf->uf_flags |= FC_NEW;
1201 nf->uf_ret_type = get_type_ptr(type_list_gap);
1202 if (nf->uf_ret_type != NULL)
1203 {
1204 nf->uf_ret_type->tt_type = VAR_OBJECT;
1205 nf->uf_ret_type->tt_class = cl;
1206 nf->uf_ret_type->tt_argcount = 0;
1207 nf->uf_ret_type->tt_args = NULL;
1208 }
1209 }
1210}
1211
1212/*
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001213 * Add the class methods and object methods to the new class "cl".
1214 * When extending a class "extends_cl", add the instance methods from the
1215 * parent class also.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001216 */
1217 static int
1218add_classfuncs_objmethods(
1219 class_T *cl,
1220 class_T *extends_cl,
1221 garray_T *classfunctions_gap,
1222 garray_T *objmethods_gap)
1223{
1224 // loop 1: class functions, loop 2: object methods
1225 for (int loop = 1; loop <= 2; ++loop)
1226 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001227 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1228 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001229 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001230 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001231 : &cl->class_obj_methods;
1232
1233 int parent_count = 0;
1234 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001235 // Include object methods from the parent.
1236 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001237 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001238 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001239 : extends_cl->class_obj_method_count;
1240
1241 *fcount = parent_count + gap->ga_len;
1242 if (*fcount == 0)
1243 {
1244 *fup = NULL;
1245 continue;
1246 }
1247 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1248 if (*fup == NULL)
1249 return FAIL;
1250
1251 if (gap->ga_len != 0)
1252 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
1253 vim_free(gap->ga_data);
1254 if (loop == 1)
1255 cl->class_class_function_count_child = gap->ga_len;
1256 else
1257 cl->class_obj_method_count_child = gap->ga_len;
1258
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001259 if (loop == 2)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001260 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001261 // Copy instance methods from the parent.
1262
1263 for (int i = 0; i < parent_count; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001264 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001265 // Can't use the same parent function, because "uf_class" is
1266 // different and compilation will have a different result.
1267 // Put them after the functions in the current class, object
1268 // methods may be overruled, then "super.Method()" is used to
1269 // find a method from the parent.
1270 ufunc_T *pf = (extends_cl->class_obj_methods)[i];
1271 (*fup)[gap->ga_len + i] = copy_function(pf);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001272
1273 // If the child class overrides a function from the parent
1274 // the signature must be equal.
1275 char_u *pname = pf->uf_name;
1276 for (int ci = 0; ci < gap->ga_len; ++ci)
1277 {
1278 ufunc_T *cf = (*fup)[ci];
1279 char_u *cname = cf->uf_name;
1280 if (STRCMP(pname, cname) == 0)
1281 {
1282 where_T where = WHERE_INIT;
1283 where.wt_func_name = (char *)pname;
1284 where.wt_kind = WT_METHOD;
1285 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1286 TRUE, where);
1287 }
1288 }
1289 }
1290 }
1291
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001292 // Set the class pointer on all the functions and object methods.
1293 for (int i = 0; i < *fcount; ++i)
1294 {
1295 ufunc_T *fp = (*fup)[i];
1296 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001297 if (i < gap->ga_len)
1298 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001299 if (loop == 2)
1300 fp->uf_flags |= FC_OBJECT;
1301 }
1302 }
1303
1304 return OK;
1305}
1306
1307/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001308 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001309 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001310 */
1311 void
1312ex_class(exarg_T *eap)
1313{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001314 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1315 long start_lnum = SOURCING_LNUM;
1316 char_u *arg = eap->arg;
1317 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001318
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001319 if (is_abstract)
1320 {
1321 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1322 {
1323 semsg(_(e_invalid_argument_str), arg);
1324 return;
1325 }
1326 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001327 is_class = TRUE;
1328 }
1329
1330 if (!current_script_is_vim9()
1331 || (cmdmod.cmod_flags & CMOD_LEGACY)
1332 || !getline_equal(eap->getline, eap->cookie, getsourceline))
1333 {
1334 if (is_class)
1335 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1336 else
1337 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1338 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001339 }
1340
1341 if (!ASCII_ISUPPER(*arg))
1342 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001343 if (is_class)
1344 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1345 else
1346 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1347 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001348 return;
1349 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001350 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1351 if (!IS_WHITE_OR_NUL(*name_end))
1352 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001353 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001354 return;
1355 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001356 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001357
Bram Moolenaara86655a2023-01-12 17:06:27 +00001358 // "export class" gets used when creating the class, don't use "is_export"
1359 // for the items inside the class.
1360 int class_export = is_export;
1361 is_export = FALSE;
1362
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001363 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001364 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001365
Bram Moolenaar83677162023-01-08 19:54:10 +00001366 // Name for "extends BaseClass"
1367 char_u *extends = NULL;
1368
Bram Moolenaar94674f22023-01-06 18:42:20 +00001369 // Names for "implements SomeInterface"
1370 garray_T ga_impl;
1371 ga_init2(&ga_impl, sizeof(char_u *), 5);
1372
1373 arg = skipwhite(name_end);
1374 while (*arg != NUL && *arg != '#' && *arg != '\n')
1375 {
1376 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001377 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001378 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1379 {
1380 if (extends != NULL)
1381 {
1382 emsg(_(e_duplicate_extends));
1383 goto early_ret;
1384 }
1385 arg = skipwhite(arg + 7);
1386 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1387 if (!IS_WHITE_OR_NUL(*end))
1388 {
1389 semsg(_(e_white_space_required_after_name_str), arg);
1390 goto early_ret;
1391 }
1392 extends = vim_strnsave(arg, end - arg);
1393 if (extends == NULL)
1394 goto early_ret;
1395
1396 arg = skipwhite(end + 1);
1397 }
1398 else if (STRNCMP(arg, "implements", 10) == 0
1399 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001400 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001401 if (!is_class)
1402 {
1403 emsg(_(e_interface_cannot_use_implements));
1404 goto early_ret;
1405 }
1406
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001407 if (ga_impl.ga_len > 0)
1408 {
1409 emsg(_(e_duplicate_implements));
1410 goto early_ret;
1411 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001412 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001413
1414 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001415 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001416 char_u *impl_end = find_name_end(arg, NULL, NULL,
1417 FNE_CHECK_START);
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001418 if ((!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1419 || (*impl_end == ','
1420 && !IS_WHITE_OR_NUL(*(impl_end + 1))))
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001421 {
1422 semsg(_(e_white_space_required_after_name_str), arg);
1423 goto early_ret;
1424 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001425 if (impl_end - arg == 0)
1426 {
1427 emsg(_(e_missing_name_after_implements));
1428 goto early_ret;
1429 }
1430
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001431 char_u *iname = vim_strnsave(arg, impl_end - arg);
1432 if (iname == NULL)
1433 goto early_ret;
1434 for (int i = 0; i < ga_impl.ga_len; ++i)
1435 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1436 {
1437 semsg(_(e_duplicate_interface_after_implements_str),
1438 iname);
1439 vim_free(iname);
1440 goto early_ret;
1441 }
1442 if (ga_add_string(&ga_impl, iname) == FAIL)
1443 {
1444 vim_free(iname);
1445 goto early_ret;
1446 }
1447 if (*impl_end != ',')
1448 {
1449 arg = skipwhite(impl_end);
1450 break;
1451 }
1452 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001453 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001454 }
1455 else
1456 {
1457 semsg(_(e_trailing_characters_str), arg);
1458early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001459 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001460 ga_clear_strings(&ga_impl);
1461 return;
1462 }
1463 }
1464
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001465 garray_T type_list; // list of pointers to allocated types
1466 ga_init2(&type_list, sizeof(type_T *), 10);
1467
Bram Moolenaard505d172022-12-18 21:42:55 +00001468 // Growarray with class members declared in the class.
1469 garray_T classmembers;
1470 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1471
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001472 // Growarray with functions declared in the class.
1473 garray_T classfunctions;
1474 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001475
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001476 // Growarray with object members declared in the class.
1477 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001478 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001479
1480 // Growarray with object methods declared in the class.
1481 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001482 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001483
1484 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001485 * Go over the body of the class/interface until "endclass" or
1486 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001487 */
1488 char_u *theline = NULL;
1489 int success = FALSE;
1490 for (;;)
1491 {
1492 vim_free(theline);
1493 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
1494 if (theline == NULL)
1495 break;
1496 char_u *line = skipwhite(theline);
1497
Bram Moolenaar418b5472022-12-20 13:38:22 +00001498 // Skip empty and comment lines.
1499 if (*line == NUL)
1500 continue;
1501 if (*line == '#')
1502 {
1503 if (vim9_bad_comment(line))
1504 break;
1505 continue;
1506 }
1507
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001508 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001509 char *end_name = is_class ? "endclass" : "endinterface";
1510 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001511 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001512 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001513 semsg(_(e_command_cannot_be_shortened_str), line);
1514 else if (*p == '|' || !ends_excmd2(line, p))
1515 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001516 else
1517 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001518 break;
1519 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001520 char *wrong_name = is_class ? "endinterface" : "endclass";
1521 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1522 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001523 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001524 break;
1525 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001526
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001527 int has_public = FALSE;
1528 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001529 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001530 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001531 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001532 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001533 break;
1534 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001535 if (!is_class)
1536 {
Yegappan Lakshmananb90e3bc2023-09-28 23:06:48 +02001537 emsg(_(e_public_variable_not_supported_in_interface));
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001538 break;
1539 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001540 has_public = TRUE;
1541 p = skipwhite(line + 6);
1542
Bram Moolenaard505d172022-12-18 21:42:55 +00001543 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001544 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001545 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001546 break;
1547 }
1548 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001549
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001550 int abstract_method = FALSE;
1551 char_u *pa = p;
1552 if (checkforcmd(&p, "abstract", 3))
1553 {
1554 if (STRNCMP(pa, "abstract", 8) != 0)
1555 {
1556 semsg(_(e_command_cannot_be_shortened_str), pa);
1557 break;
1558 }
1559
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001560 if (!is_class)
1561 // ignore "abstract" in an interface (as all the methods in an
1562 // interface are abstract.
1563 p = skipwhite(pa + 8);
1564 else
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001565 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001566 if (!is_abstract)
1567 {
1568 semsg(_(e_abstract_method_in_concrete_class), pa);
1569 break;
1570 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001571
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001572 abstract_method = TRUE;
1573 p = skipwhite(pa + 8);
1574 if (STRNCMP(p, "def", 3) != 0 && STRNCMP(p, "static", 6) != 0)
1575 {
1576 emsg(_(e_abstract_must_be_followed_by_def_or_static));
1577 break;
1578 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001579 }
1580 }
1581
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001582 int has_static = FALSE;
1583 char_u *ps = p;
1584 if (checkforcmd(&p, "static", 4))
1585 {
1586 if (STRNCMP(ps, "static", 6) != 0)
1587 {
1588 semsg(_(e_command_cannot_be_shortened_str), ps);
1589 break;
1590 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001591
1592 if (!is_class)
1593 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001594 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001595 break;
1596 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001597 has_static = TRUE;
1598 p = skipwhite(ps + 6);
1599 }
1600
Bram Moolenaard505d172022-12-18 21:42:55 +00001601 // object members (public, read access, private):
1602 // "this._varname"
1603 // "this.varname"
1604 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001605 if (STRNCMP(p, "this", 4) == 0)
1606 {
1607 if (p[4] != '.' || !eval_isnamec1(p[5]))
1608 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001609 semsg(_(e_invalid_object_variable_declaration_str), p);
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001610 break;
1611 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001612 if (has_static)
1613 {
1614 emsg(_(e_static_cannot_be_followed_by_this));
1615 break;
1616 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001617 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +00001618 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001619 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001620 char_u *init_expr = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001621 int has_type = FALSE;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001622
1623 if (!is_class && *varname == '_')
1624 {
1625 // private variables are not supported in an interface
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001626 semsg(_(e_private_variable_not_supported_in_interface),
1627 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001628 break;
1629 }
1630
Bram Moolenaard505d172022-12-18 21:42:55 +00001631 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001632 &varname_end, &has_type, &type_list, &type,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001633 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001634 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001635 if (is_reserved_varname(varname, varname_end))
1636 {
1637 vim_free(init_expr);
1638 break;
1639 }
1640 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1641 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001642 {
1643 vim_free(init_expr);
1644 break;
1645 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001646 if (add_member(&objmembers, varname, varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001647 has_public, has_type, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001648 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001649 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001650 break;
1651 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001652 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001653
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001654 // constructors:
1655 // def new()
1656 // enddef
1657 // def newOther()
1658 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001659 // object methods and class functions:
1660 // def SomeMethod()
1661 // enddef
1662 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001663 // enddef
1664 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001665 // def <Tval> someMethod()
1666 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001667 else if (checkforcmd(&p, "def", 3))
1668 {
1669 exarg_T ea;
1670 garray_T lines_to_free;
1671
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001672 if (has_public)
1673 {
1674 // "public" keyword is not supported when defining an object or
1675 // class method
1676 emsg(_(e_public_keyword_not_supported_for_method));
1677 break;
1678 }
1679
1680 if (*p == NUL)
1681 {
1682 // No method name following def
1683 semsg(_(e_not_valid_command_in_class_str), line);
1684 break;
1685 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001686
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001687 CLEAR_FIELD(ea);
1688 ea.cmd = line;
1689 ea.arg = p;
1690 ea.cmdidx = CMD_def;
1691 ea.getline = eap->getline;
1692 ea.cookie = eap->cookie;
1693
1694 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001695 int class_flags;
1696 if (is_class)
1697 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
1698 else
1699 class_flags = CF_INTERFACE;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001700 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02001701 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001702 ga_clear_strings(&lines_to_free);
1703
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001704 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001705 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001706 char_u *name = uf->uf_name;
1707 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001708
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001709 if (!is_class && *name == '_')
1710 {
1711 // private variables are not supported in an interface
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001712 semsg(_(e_private_method_not_supported_in_interface),
1713 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001714 func_clear_free(uf, FALSE);
1715 break;
1716 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001717 if (is_new && !is_valid_constructor(uf, is_abstract,
1718 has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001719 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001720 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001721 break;
1722 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001723
Bram Moolenaar58b40092023-01-11 15:59:05 +00001724 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001725 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001726 {
1727 success = FALSE;
1728 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001729 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001730 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001731
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001732 garray_T *fgap = has_static || is_new
1733 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001734 if (ga_grow(fgap, 1) == OK)
1735 {
1736 if (is_new)
1737 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001738
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001739 if (abstract_method)
1740 uf->uf_flags |= FC_ABSTRACT;
1741
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001742 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1743 ++fgap->ga_len;
1744 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001745 }
1746 }
1747
1748 // class members
1749 else if (has_static)
1750 {
1751 // class members (public, read access, private):
1752 // "static _varname"
1753 // "static varname"
1754 // "public static varname"
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001755 char_u *varname = p;
1756 char_u *varname_end = NULL;
1757 int has_type = FALSE;
1758 type_T *type = NULL;
1759 char_u *init_expr = NULL;
1760
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001761 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001762 &varname_end, &has_type, &type_list, &type,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001763 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001764 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001765 if (is_reserved_varname(varname, varname_end))
1766 {
1767 vim_free(init_expr);
1768 break;
1769 }
1770 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1771 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001772 {
1773 vim_free(init_expr);
1774 break;
1775 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001776 if (add_member(&classmembers, varname, varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001777 has_public, has_type, type, init_expr) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001778 {
1779 vim_free(init_expr);
1780 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001781 }
1782 }
1783
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001784 else
1785 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001786 if (is_class)
1787 semsg(_(e_not_valid_command_in_class_str), line);
1788 else
1789 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001790 break;
1791 }
1792 }
1793 vim_free(theline);
1794
Bram Moolenaar83677162023-01-08 19:54:10 +00001795 class_T *extends_cl = NULL; // class from "extends" argument
1796
1797 /*
1798 * Check a few things before defining the class.
1799 */
1800
1801 // Check the "extends" class is valid.
1802 if (success && extends != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001803 success = validate_extends_class(extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00001804 VIM_CLEAR(extends);
1805
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001806 // Check the new object methods to make sure their access (public or
1807 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001808 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001809 success = validate_extends_methods(&objmethods, extends_cl);
1810
1811 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001812 // variables in the extended class lineage. If an interface is extending
1813 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001814 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001815 {
1816 if (is_class)
1817 success = extends_check_dup_members(&objmembers, extends_cl);
1818 else
1819 success = extends_check_intf_var_type(&objmembers, extends_cl);
1820 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001821
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001822 // When extending an abstract class, make sure all the abstract methods in
1823 // the parent class are implemented. If the current class is an abstract
1824 // class, then there is no need for this check.
1825 if (success && !is_abstract && extends_cl != NULL
1826 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001827 success = validate_abstract_class_methods(&classfunctions,
1828 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001829
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001830 class_T **intf_classes = NULL;
1831
Bram Moolenaar83677162023-01-08 19:54:10 +00001832 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001833 if (success && ga_impl.ga_len > 0)
1834 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001835 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1836
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001837 success = validate_implements_classes(&ga_impl, intf_classes,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02001838 &objmethods, &objmembers, extends_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001839 }
1840
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001841 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001842 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001843 success = check_func_arg_names(&classfunctions, &objmethods,
1844 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001845
Bram Moolenaareb533502022-12-14 15:06:11 +00001846 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001847 if (success)
1848 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001849 // "endclass" encountered without failures: Create the class.
1850
Bram Moolenaareb533502022-12-14 15:06:11 +00001851 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001852 if (cl == NULL)
1853 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001854 if (!is_class)
1855 cl->class_flags = CLASS_INTERFACE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001856 else if (is_abstract)
1857 cl->class_flags = CLASS_ABSTRACT;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001858
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001859 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001860 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001861 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001862 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001863
Bram Moolenaard0200c82023-01-28 15:19:40 +00001864 if (extends_cl != NULL)
1865 {
1866 cl->class_extends = extends_cl;
1867 extends_cl->class_flags |= CLASS_EXTENDED;
1868 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001869
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001870 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00001871 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001872 NULL,
1873 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00001874 &cl->class_class_members,
1875 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001876 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001877 extends_cl == NULL ? NULL
1878 : extends_cl->class_obj_members,
1879 extends_cl == NULL ? 0
1880 : extends_cl->class_obj_member_count,
1881 &cl->class_obj_members,
1882 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001883 goto cleanup;
1884
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001885 if (ga_impl.ga_len > 0)
1886 {
1887 // Move the "implements" names into the class.
1888 cl->class_interface_count = ga_impl.ga_len;
1889 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1890 if (cl->class_interfaces == NULL)
1891 goto cleanup;
1892 for (int i = 0; i < ga_impl.ga_len; ++i)
1893 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1894 VIM_CLEAR(ga_impl.ga_data);
1895 ga_impl.ga_len = 0;
1896
Bram Moolenaard0200c82023-01-28 15:19:40 +00001897 cl->class_interfaces_cl = intf_classes;
1898 intf_classes = NULL;
1899 }
1900
1901 if (cl->class_interface_count > 0 || extends_cl != NULL)
1902 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001903 // Add a method and member lookup table to each of the interface
1904 // classes.
1905 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1906 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001907 }
1908
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001909 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001910 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001911 add_class_members(cl, eap, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001912
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001913 int have_new = FALSE;
1914 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001915 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001916 {
1917 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1918 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001919 {
1920 have_new = TRUE;
1921 break;
1922 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001923 }
1924
1925 if (have_new)
1926 // The return type of new() is an object of class "cl"
1927 class_func->uf_ret_type->tt_class = cl;
1928 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001929 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001930 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001931
Bram Moolenaar58b40092023-01-11 15:59:05 +00001932 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001933 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1934 &objmethods) == FAIL)
1935 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001936
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001937 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001938 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001939 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001940 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001941 cl->class_type_list = type_list;
1942
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001943 class_created(cl);
1944
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001945 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001946 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001947
1948 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001949 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02001950 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001951 typval_T tv;
1952 tv.v_type = VAR_CLASS;
1953 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001954 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001955 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001956 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001957 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001958 return;
1959 }
1960
1961cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00001962 if (cl != NULL)
1963 {
1964 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001965 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001966 if (cl->class_interfaces != NULL)
1967 {
1968 for (int i = 0; i < cl->class_interface_count; ++i)
1969 vim_free(cl->class_interfaces[i]);
1970 vim_free(cl->class_interfaces);
1971 }
1972 if (cl->class_interfaces_cl != NULL)
1973 {
1974 for (int i = 0; i < cl->class_interface_count; ++i)
1975 class_unref(cl->class_interfaces_cl[i]);
1976 vim_free(cl->class_interfaces_cl);
1977 }
Bram Moolenaareb533502022-12-14 15:06:11 +00001978 vim_free(cl->class_obj_members);
1979 vim_free(cl->class_obj_methods);
1980 vim_free(cl);
1981 }
1982
Bram Moolenaar83677162023-01-08 19:54:10 +00001983 vim_free(extends);
1984 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001985
1986 if (intf_classes != NULL)
1987 {
1988 for (int i = 0; i < ga_impl.ga_len; ++i)
1989 class_unref(intf_classes[i]);
1990 vim_free(intf_classes);
1991 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001992 ga_clear_strings(&ga_impl);
1993
Bram Moolenaard505d172022-12-18 21:42:55 +00001994 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001995 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001996 garray_T *gap = round == 1 ? &classmembers : &objmembers;
1997 if (gap->ga_len == 0 || gap->ga_data == NULL)
1998 continue;
1999
2000 for (int i = 0; i < gap->ga_len; ++i)
2001 {
2002 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2003 vim_free(m->ocm_name);
2004 vim_free(m->ocm_init);
2005 }
2006 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002007 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002008
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002009 for (int i = 0; i < objmethods.ga_len; ++i)
2010 {
2011 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2012 func_clear_free(uf, FALSE);
2013 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002014 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002015
2016 for (int i = 0; i < classfunctions.ga_len; ++i)
2017 {
2018 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2019 func_clear_free(uf, FALSE);
2020 }
2021 ga_clear(&classfunctions);
2022
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002023 clear_type_list(&type_list);
2024}
2025
2026/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002027 * Find member "name" in class "cl", set "member_idx" to the member index and
2028 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002029 * When "is_object" is TRUE, then look for object members. Otherwise look for
2030 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002031 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002032 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002033 */
2034 type_T *
2035class_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002036 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002037 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002038 char_u *name,
2039 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002040 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002041{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002042 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002043 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002044
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002045 *member_idx = -1; // not found (yet)
2046
2047 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2048 member_idx);
2049 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002050 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002051 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2052 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002053 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002054 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002055
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002056 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002057}
2058
2059/*
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002060 * Given a class or object variable index, return the variable type
2061 */
2062 type_T *
2063class_member_type_by_idx(
2064 class_T *cl,
2065 int is_object,
2066 int member_idx)
2067{
2068 ocmember_T *m;
2069 int member_count;
2070
2071 if (is_object)
2072 {
2073 m = cl->class_obj_members;
2074 member_count = cl->class_obj_member_count;
2075 }
2076 else
2077 {
2078 m = cl->class_class_members;
2079 member_count = cl->class_class_member_count;
2080 }
2081
2082 if (member_idx >= member_count)
2083 return NULL;
2084
2085 return m[member_idx].ocm_type;
2086}
2087
2088/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002089 * Handle ":enum" up to ":endenum".
2090 */
2091 void
2092ex_enum(exarg_T *eap UNUSED)
2093{
2094 // TODO
2095}
2096
2097/*
2098 * Handle ":type".
2099 */
2100 void
2101ex_type(exarg_T *eap UNUSED)
2102{
2103 // TODO
2104}
2105
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002106/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002107 * Returns OK if a member variable named "name" is present in the class "cl".
2108 * Otherwise returns FAIL. If found, the member variable typval is set in
2109 * "rettv". If "is_object" is TRUE, then the object member variable table is
2110 * searched. Otherwise the class member variable table is searched.
2111 */
2112 static int
2113get_member_tv(
2114 class_T *cl,
2115 int is_object,
2116 char_u *name,
2117 size_t namelen,
2118 typval_T *rettv)
2119{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002120 ocmember_T *m;
2121 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002122
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002123 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2124 &m_idx);
2125 if (m == NULL)
2126 return FAIL;
2127
2128 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002129 {
Ernie Raele6c9aa52023-10-06 19:55:52 +02002130 emsg_var_cl_define(e_cannot_access_private_variable_str,
2131 m->ocm_name, 0, cl);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002132 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002133 }
2134
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002135 if (is_object)
2136 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002137 // The object only contains a pointer to the class, the member values
2138 // array follows right after that.
2139 object_T *obj = rettv->vval.v_object;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002140 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2141 copy_tv(tv, rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002142 object_unref(obj);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002143 }
2144 else
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002145 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002146 copy_tv(&cl->class_members_tv[m_idx], rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002147 class_unref(cl);
2148 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002149
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002150 return OK;
2151}
2152
2153/*
2154 * Call an object or class method "name" in class "cl". The method return
2155 * value is returned in "rettv".
2156 */
2157 static int
2158call_oc_method(
2159 class_T *cl,
2160 char_u *name,
2161 size_t len,
2162 char_u *name_end,
2163 evalarg_T *evalarg,
2164 char_u **arg,
2165 typval_T *rettv)
2166{
2167 ufunc_T *fp;
2168 typval_T argvars[MAX_FUNC_ARGS + 1];
2169 int argcount = 0;
2170
2171 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
2172 if (fp == NULL)
2173 {
2174 method_not_found_msg(cl, rettv->v_type, name, len);
2175 return FAIL;
2176 }
2177
2178 if (*fp->uf_name == '_')
2179 {
2180 // Cannot access a private method outside of a class
2181 semsg(_(e_cannot_access_private_method_str), fp->uf_name);
2182 return FAIL;
2183 }
2184
2185 char_u *argp = name_end;
2186 int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount);
2187 if (ret == FAIL)
2188 return FAIL;
2189
2190 funcexe_T funcexe;
2191 CLEAR_FIELD(funcexe);
2192 funcexe.fe_evaluate = TRUE;
2193 if (rettv->v_type == VAR_OBJECT)
2194 {
2195 funcexe.fe_object = rettv->vval.v_object;
2196 ++funcexe.fe_object->obj_refcount;
2197 }
2198
2199 // Clear the class or object after calling the function, in
2200 // case the refcount is one.
2201 typval_T tv_tofree = *rettv;
2202 rettv->v_type = VAR_UNKNOWN;
2203
2204 // Call the user function. Result goes into rettv;
2205 int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
2206 NULL);
2207
2208 // Clear the previous rettv and the arguments.
2209 clear_tv(&tv_tofree);
2210 for (int idx = 0; idx < argcount; ++idx)
2211 clear_tv(&argvars[idx]);
2212
2213 if (error != FCERR_NONE)
2214 {
2215 user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
2216 return FAIL;
2217 }
2218 *arg = argp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002219
2220 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002221}
2222
2223/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002224 * Evaluate what comes after a class:
2225 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002226 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002227 * - class constructor: SomeClass.new()
2228 * - object member: someObject.varname
2229 * - object method: someObject.SomeMethod()
2230 *
2231 * "*arg" points to the '.'.
2232 * "*arg" is advanced to after the member name or method call.
2233 *
2234 * Returns FAIL or OK.
2235 */
2236 int
2237class_object_index(
2238 char_u **arg,
2239 typval_T *rettv,
2240 evalarg_T *evalarg,
2241 int verbose UNUSED) // give error messages
2242{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002243 if (VIM_ISWHITE((*arg)[1]))
2244 {
2245 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
2246 return FAIL;
2247 }
2248
2249 ++*arg;
2250 char_u *name = *arg;
2251 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2252 if (name_end == name)
2253 return FAIL;
2254 size_t len = name_end - name;
2255
Ernie Raeld615a312023-10-05 20:28:16 +02002256 int did_emsg_save = did_emsg;
Bram Moolenaar552bdca2023-02-17 21:08:50 +00002257 class_T *cl;
2258 if (rettv->v_type == VAR_CLASS)
2259 cl = rettv->vval.v_class;
2260 else // VAR_OBJECT
2261 {
2262 if (rettv->vval.v_object == NULL)
2263 {
2264 emsg(_(e_using_null_object));
2265 return FAIL;
2266 }
2267 cl = rettv->vval.v_object->obj_class;
2268 }
2269
Bram Moolenaard13dd302023-03-11 20:56:35 +00002270 if (cl == NULL)
2271 {
2272 emsg(_(e_incomplete_type));
2273 return FAIL;
2274 }
2275
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002276 if (*name_end == '(')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002277 // Invoke the class or object method
2278 return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002279
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002280 else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002281 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002282 // Search in the object member variable table and the class member
2283 // variable table.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002284 int is_object = rettv->v_type == VAR_OBJECT;
2285 if (get_member_tv(cl, is_object, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002286 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002287 *arg = name_end;
2288 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002289 }
2290
Ernie Raeld615a312023-10-05 20:28:16 +02002291 if (did_emsg == did_emsg_save)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002292 member_not_found_msg(cl, is_object, name, len);
Bram Moolenaard505d172022-12-18 21:42:55 +00002293 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002294
2295 return FAIL;
2296}
2297
2298/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002299 * If "arg" points to a class or object method, return it.
2300 * Otherwise return NULL.
2301 */
2302 ufunc_T *
2303find_class_func(char_u **arg)
2304{
2305 char_u *name = *arg;
2306 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2307 if (name_end == name || *name_end != '.')
2308 return NULL;
2309
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002310 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002311 size_t len = name_end - name;
2312 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002313 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00002314 if (eval_variable(name, (int)len,
2315 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002316 return NULL;
2317 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00002318 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002319
2320 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
2321 : tv.vval.v_object->obj_class;
2322 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002323 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002324 char_u *fname = name_end + 1;
2325 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
2326 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00002327 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002328 len = fname_end - fname;
2329
Ernie Rael4d00b832023-09-11 19:54:42 +02002330 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002331
Bram Moolenaareb533502022-12-14 15:06:11 +00002332fail_after_eval:
2333 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002334 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002335}
2336
2337/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002338 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002339 * Returns -1, if the variable is not found.
2340 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002341 */
2342 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002343class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002344{
Ernie Rael4d00b832023-09-11 19:54:42 +02002345 int idx;
2346 class_member_lookup(cl, name, namelen, &idx);
2347 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002348}
2349
2350/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002351 * Returns a pointer to the class member variable "name" in the class "cl".
2352 * Returns NULL if the variable is not found.
2353 * The member variable index is set in "idx".
2354 */
2355 ocmember_T *
2356class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2357{
Ernie Rael4d00b832023-09-11 19:54:42 +02002358 ocmember_T *ret_m = NULL;
2359 int ret_idx = -1;
2360 for (int i = 0; i < cl->class_class_member_count; ++i)
2361 {
2362 ocmember_T *m = &cl->class_class_members[i];
2363 if (namelen)
2364 {
2365 if (STRNCMP(name, m->ocm_name, namelen) == 0
2366 && m->ocm_name[namelen] == NUL)
2367 {
2368 ret_m = m;
2369 ret_idx = i;
2370 break;
2371 }
2372 }
2373 else if (STRCMP(name, m->ocm_name) == 0)
2374 {
2375 ret_m = m;
2376 ret_idx = i;
2377 break;
2378 }
2379 }
2380 if (idx != NULL)
2381 *idx = ret_idx;
2382 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002383}
2384
2385/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002386 * Returns a pointer to the class method "name" in class "cl".
2387 * Returns NULL if the method is not found.
2388 * The method index is set in "idx".
2389 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002390 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002391class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2392{
Ernie Rael4d00b832023-09-11 19:54:42 +02002393 ufunc_T *ret_fp = NULL;
2394 int ret_idx = -1;
2395 for (int i = 0; i < cl->class_class_function_count; ++i)
2396 {
2397 ufunc_T *fp = cl->class_class_functions[i];
2398 char_u *ufname = (char_u *)fp->uf_name;
2399 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2400 {
2401 ret_fp = fp;
2402 ret_idx = i;
2403 break;
2404 }
2405 }
2406 if (idx != NULL)
2407 *idx = ret_idx;
2408 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002409}
2410
2411/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002412 * Returns the index of class method "name" in the class "cl".
2413 * Returns -1, if the method is not found.
2414 */
2415 int
2416class_method_idx(class_T *cl, char_u *name, size_t namelen)
2417{
2418 int idx;
2419 class_method_lookup(cl, name, namelen, &idx);
2420 return idx;
2421}
2422
2423/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002424 * Returns the index of object member variable "name" in the class "cl".
2425 * Returns -1, if the variable is not found.
2426 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
2427 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002428 static int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002429object_member_idx(class_T *cl, char_u *name, size_t namelen)
2430{
Ernie Rael4d00b832023-09-11 19:54:42 +02002431 int idx;
2432 object_member_lookup(cl, name, namelen, &idx);
2433 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002434}
2435
2436/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002437 * Returns a pointer to the object member variable "name" in the class "cl".
2438 * Returns NULL if the variable is not found.
2439 * The object member variable index is set in "idx".
2440 */
2441 ocmember_T *
2442object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2443{
Ernie Rael4d00b832023-09-11 19:54:42 +02002444 ocmember_T *ret_m = NULL;
2445 int ret_idx = -1;
2446 for (int i = 0; i < cl->class_obj_member_count; ++i)
2447 {
2448 ocmember_T *m = &cl->class_obj_members[i];
2449 if (namelen)
2450 {
2451 if (STRNCMP(name, m->ocm_name, namelen) == 0
2452 && m->ocm_name[namelen] == NUL)
2453 {
2454 ret_m = m;
2455 ret_idx = i;
2456 break;
2457 }
2458 }
2459 else if (STRCMP(name, m->ocm_name) == 0)
2460 {
2461 ret_m = m;
2462 ret_idx = i;
2463 break;
2464 }
2465 }
2466 if (idx != NULL)
2467 *idx = ret_idx;
2468 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002469}
2470
2471/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002472 * Returns a pointer to the object method "name" in class "cl".
2473 * Returns NULL if the method is not found.
2474 * The object method index is set in "idx".
2475 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002476 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002477object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2478{
Ernie Rael4d00b832023-09-11 19:54:42 +02002479 ufunc_T *ret_fp = NULL;
2480 int ret_idx = -1;
2481 for (int i = 0; i < cl->class_obj_method_count; ++i)
2482 {
2483 ufunc_T *fp = cl->class_obj_methods[i];
2484 // Use a separate pointer to avoid that ASAN complains about
2485 // uf_name[] only being 4 characters.
2486 char_u *ufname = (char_u *)fp->uf_name;
2487 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2488 {
2489 ret_fp = fp;
2490 ret_idx = i;
2491 break;
2492 }
2493 }
2494 if (idx != NULL)
2495 *idx = ret_idx;
2496 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002497}
2498
2499/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002500 * Returns the index of object method "name" in the class "cl".
2501 * Returns -1, if the method is not found.
2502 */
2503 int
2504object_method_idx(class_T *cl, char_u *name, size_t namelen)
2505{
2506 int idx;
2507 object_method_lookup(cl, name, namelen, &idx);
2508 return idx;
2509}
2510
2511/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002512 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
2513 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
2514 * object member variable.
2515 *
2516 * Returns a pointer to the member variable structure if variable is found.
2517 * Otherwise returns NULL. The member variable index is set in "*idx".
2518 */
2519 ocmember_T *
2520member_lookup(
2521 class_T *cl,
2522 vartype_T v_type,
2523 char_u *name,
2524 size_t namelen,
2525 int *idx)
2526{
2527 if (v_type == VAR_CLASS)
2528 return class_member_lookup(cl, name, namelen, idx);
2529 else
2530 return object_member_lookup(cl, name, namelen, idx);
2531}
2532
2533/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02002534 * Find the class that defines the named member. Look up the hierarchy
2535 * starting at "cl".
2536 *
2537 * Return the class that defines the member "name", else NULL.
2538 * Fill in "p_m", if specified, for ocmember_T in found class.
2539 */
2540// NOTE: if useful for something could also indirectly return vartype and idx.
2541 static class_T *
2542class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m)
2543{
2544 class_T *cl_found = NULL;
2545 vartype_T vartype = VAR_UNKNOWN;
2546 ocmember_T *m_found = NULL;
2547
2548 len = len != 0 ? len : STRLEN(name);
2549
2550 // Loop assumes if member is not defined in "cl", then it is not
2551 // defined in any super class; the last class where it's found is the
2552 // class where it is defined. Once the vartype is found, the other
2553 // type is no longer checked.
2554 for (class_T *super = cl; super != NULL; super = super->class_extends)
2555 {
2556 class_T *cl_tmp = NULL;
2557 ocmember_T *m = NULL;
2558 if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT)
2559 {
2560 if ((m = object_member_lookup(super, name, len, NULL)) != NULL)
2561 {
2562 cl_tmp = super;
2563 vartype = VAR_OBJECT;
2564 }
2565 }
2566 if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS)
2567 {
2568 if (( m = class_member_lookup(super, name, len, NULL)) != NULL)
2569 {
2570 cl_tmp = super;
2571 vartype = VAR_OBJECT;
2572 }
2573 }
2574 if (cl_tmp == NULL)
2575 break; // member is not in this or any super class.
2576 cl_found = cl_tmp;
2577 m_found = m;
2578 }
2579 if (p_m != NULL)
2580 *p_m = m_found;
2581 return cl_found;
2582}
2583
2584/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002585 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
2586 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
2587 *
2588 * Returns a pointer to the method structure if variable is found.
2589 * Otherwise returns NULL. The method variable index is set in "*idx".
2590 */
2591 ufunc_T *
2592method_lookup(
2593 class_T *cl,
2594 vartype_T v_type,
2595 char_u *name,
2596 size_t namelen,
2597 int *idx)
2598{
2599 if (v_type == VAR_CLASS)
2600 return class_method_lookup(cl, name, namelen, idx);
2601 else
2602 return object_method_lookup(cl, name, namelen, idx);
2603}
2604
2605/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00002606 * Return TRUE if current context "cctx_arg" is inside class "cl".
2607 * Return FALSE if not.
2608 */
2609 int
2610inside_class(cctx_T *cctx_arg, class_T *cl)
2611{
2612 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002613 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002614 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002615 return TRUE;
2616 return FALSE;
2617}
2618
2619/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002620 * Make a copy of an object.
2621 */
2622 void
2623copy_object(typval_T *from, typval_T *to)
2624{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002625 if (from->vval.v_object == NULL)
2626 to->vval.v_object = NULL;
2627 else
2628 {
2629 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002630 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002631 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002632}
2633
2634/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002635 * Make a copy of a class.
2636 */
2637 void
2638copy_class(typval_T *from, typval_T *to)
2639{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002640 if (from->vval.v_class == NULL)
2641 to->vval.v_class = NULL;
2642 else
2643 {
2644 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002645 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002646 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002647}
2648
2649/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002650 * Free the class "cl" and its contents.
2651 */
2652 static void
2653class_free(class_T *cl)
2654{
2655 // Freeing what the class contains may recursively come back here.
2656 // Clear "class_name" first, if it is NULL the class does not need to
2657 // be freed.
2658 VIM_CLEAR(cl->class_name);
2659
2660 class_unref(cl->class_extends);
2661
2662 for (int i = 0; i < cl->class_interface_count; ++i)
2663 {
2664 vim_free(((char_u **)cl->class_interfaces)[i]);
2665 if (cl->class_interfaces_cl[i] != NULL)
2666 class_unref(cl->class_interfaces_cl[i]);
2667 }
2668 vim_free(cl->class_interfaces);
2669 vim_free(cl->class_interfaces_cl);
2670
2671 itf2class_T *next;
2672 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2673 {
2674 next = i2c->i2c_next;
2675 vim_free(i2c);
2676 }
2677
2678 for (int i = 0; i < cl->class_class_member_count; ++i)
2679 {
2680 ocmember_T *m = &cl->class_class_members[i];
2681 vim_free(m->ocm_name);
2682 vim_free(m->ocm_init);
2683 if (cl->class_members_tv != NULL)
2684 clear_tv(&cl->class_members_tv[i]);
2685 }
2686 vim_free(cl->class_class_members);
2687 vim_free(cl->class_members_tv);
2688
2689 for (int i = 0; i < cl->class_obj_member_count; ++i)
2690 {
2691 ocmember_T *m = &cl->class_obj_members[i];
2692 vim_free(m->ocm_name);
2693 vim_free(m->ocm_init);
2694 }
2695 vim_free(cl->class_obj_members);
2696
2697 for (int i = 0; i < cl->class_class_function_count; ++i)
2698 {
2699 ufunc_T *uf = cl->class_class_functions[i];
2700 func_clear_free(uf, FALSE);
2701 }
2702 vim_free(cl->class_class_functions);
2703
2704 for (int i = 0; i < cl->class_obj_method_count; ++i)
2705 {
2706 ufunc_T *uf = cl->class_obj_methods[i];
2707 func_clear_free(uf, FALSE);
2708 }
2709 vim_free(cl->class_obj_methods);
2710
2711 clear_type_list(&cl->class_type_list);
2712
2713 class_cleared(cl);
2714
2715 vim_free(cl);
2716}
2717
2718/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002719 * Unreference a class. Free it when the reference count goes down to zero.
2720 */
2721 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002722class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002723{
Bram Moolenaard505d172022-12-18 21:42:55 +00002724 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002725 class_free(cl);
2726}
2727
2728/*
2729 * Go through the list of all classes and free items without "copyID".
2730 */
2731 int
2732class_free_nonref(int copyID)
2733{
2734 int did_free = FALSE;
2735
2736 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002737 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002738 next_nonref_class = cl->class_next_used;
2739 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002740 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002741 // Free the class and items it contains.
2742 class_free(cl);
2743 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002744 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002745 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002746
2747 next_nonref_class = NULL;
2748 return did_free;
2749}
2750
2751 int
2752set_ref_in_classes(int copyID)
2753{
2754 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
2755 set_ref_in_item_class(cl, copyID, NULL, NULL);
2756
2757 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002758}
2759
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002760static object_T *first_object = NULL;
2761
2762/*
2763 * Call this function when an object has been created. It will be added to the
2764 * list headed by "first_object".
2765 */
2766 void
2767object_created(object_T *obj)
2768{
2769 if (first_object != NULL)
2770 {
2771 obj->obj_next_used = first_object;
2772 first_object->obj_prev_used = obj;
2773 }
2774 first_object = obj;
2775}
2776
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002777static object_T *next_nonref_obj = NULL;
2778
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002779/*
2780 * Call this function when an object has been cleared and is about to be freed.
2781 * It is removed from the list headed by "first_object".
2782 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002783 static void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002784object_cleared(object_T *obj)
2785{
2786 if (obj->obj_next_used != NULL)
2787 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
2788 if (obj->obj_prev_used != NULL)
2789 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
2790 else if (first_object == obj)
2791 first_object = obj->obj_next_used;
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002792
2793 // update the next object to check if needed
2794 if (obj == next_nonref_obj)
2795 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002796}
2797
2798/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002799 * Free an object.
2800 */
2801 static void
2802object_clear(object_T *obj)
2803{
2804 // Avoid a recursive call, it can happen if "obj" has a circular reference.
2805 obj->obj_refcount = INT_MAX;
2806
2807 class_T *cl = obj->obj_class;
2808
2809 if (!cl)
2810 return;
2811
2812 // the member values are just after the object structure
2813 typval_T *tv = (typval_T *)(obj + 1);
2814 for (int i = 0; i < cl->class_obj_member_count; ++i)
2815 clear_tv(tv + i);
2816
2817 // Remove from the list headed by "first_object".
2818 object_cleared(obj);
2819
2820 vim_free(obj);
2821 class_unref(cl);
2822}
2823
2824/*
2825 * Unreference an object.
2826 */
2827 void
2828object_unref(object_T *obj)
2829{
2830 if (obj != NULL && --obj->obj_refcount <= 0)
2831 object_clear(obj);
2832}
2833
2834/*
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002835 * Go through the list of all objects and free items without "copyID".
2836 */
2837 int
2838object_free_nonref(int copyID)
2839{
2840 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002841
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002842 for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002843 {
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002844 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002845 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
2846 {
2847 // Free the object and items it contains.
2848 object_clear(obj);
2849 did_free = TRUE;
2850 }
2851 }
2852
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002853 next_nonref_obj = NULL;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002854 return did_free;
2855}
2856
LemonBoyafe04662023-08-23 21:08:11 +02002857/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02002858 * Output message which takes a variable name and the class that defines it.
2859 * "cl" is that class where the name was found. Search "cl"'s hierarchy to
2860 * find the defining class.
2861 */
2862 void
2863emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl)
2864{
2865 ocmember_T *m;
2866 class_T *cl_def = class_defining_member(cl, name, len, &m);
2867 if (cl_def != NULL)
2868 semsg(_(msg), m->ocm_name, cl_def->class_name);
2869 else
2870 emsg(_(e_internal_error_please_report_a_bug));
2871}
2872
2873/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002874 * Echo a class or object method not found message.
2875 */
2876 void
2877method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
2878{
2879 char_u *method_name = vim_strnsave(name, len);
2880 if ((v_type == VAR_OBJECT)
2881 && (class_method_idx(cl, name, len) >= 0))
2882 {
2883 // If this is a class method, then give a different error
2884 if (*name == '_')
2885 semsg(_(e_cannot_access_private_method_str), method_name);
2886 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002887 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002888 method_name, cl->class_name);
2889 }
2890 else if ((v_type == VAR_CLASS)
2891 && (object_method_idx(cl, name, len) >= 0))
2892 {
2893 // If this is an object method, then give a different error
2894 if (*name == '_')
2895 semsg(_(e_cannot_access_private_method_str), method_name);
2896 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002897 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002898 method_name, cl->class_name);
2899 }
2900 else
2901 semsg(_(e_method_not_found_on_class_str_str), cl->class_name,
2902 method_name);
2903 vim_free(method_name);
2904}
2905
2906/*
2907 * Echo a class or object member not found message.
2908 */
2909 void
2910member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
2911{
2912 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
2913
2914 if (v_type == VAR_OBJECT)
2915 {
2916 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02002917 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002918 varname, cl->class_name);
2919 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002920 semsg(_(e_variable_not_found_on_object_str_str), cl->class_name,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002921 varname);
2922 }
2923 else
2924 {
2925 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02002926 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002927 varname, cl->class_name);
2928 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002929 semsg(_(e_class_variable_str_not_found_in_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002930 varname, cl->class_name);
2931 }
2932 vim_free(varname);
2933}
2934
2935/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002936 * Return TRUE when the class "cl", its base class or one of the implemented
2937 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02002938 */
2939 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002940class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02002941{
2942 if (cl == other_cl)
2943 return TRUE;
2944
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002945 // Recursively check the base classes.
2946 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02002947 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002948 if (cl == other_cl)
2949 return TRUE;
2950 // Check the implemented interfaces and the super interfaces
2951 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002952 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002953 class_T *intf = cl->class_interfaces_cl[i];
2954 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002955 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002956 if (intf == other_cl)
2957 return TRUE;
2958 // check the super interfaces
2959 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002960 }
2961 }
LemonBoyafe04662023-08-23 21:08:11 +02002962 }
2963
2964 return FALSE;
2965}
2966
2967/*
2968 * "instanceof(object, classinfo)" function
2969 */
2970 void
2971f_instanceof(typval_T *argvars, typval_T *rettv)
2972{
2973 typval_T *object_tv = &argvars[0];
2974 typval_T *classinfo_tv = &argvars[1];
2975 listitem_T *li;
2976
2977 rettv->vval.v_number = VVAL_FALSE;
2978
2979 if (check_for_object_arg(argvars, 0) == FAIL
2980 || check_for_class_or_list_arg(argvars, 1) == FAIL)
2981 return;
2982
Ernie Rael3da696d2023-09-19 20:14:18 +02002983 if (object_tv->vval.v_object == NULL)
2984 return;
2985
LemonBoyafe04662023-08-23 21:08:11 +02002986 if (classinfo_tv->v_type == VAR_LIST)
2987 {
2988 FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
2989 {
2990 if (li->li_tv.v_type != VAR_CLASS)
2991 {
2992 emsg(_(e_class_required));
2993 return;
2994 }
2995
2996 if (class_instance_of(object_tv->vval.v_object->obj_class,
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002997 li->li_tv.vval.v_class) == TRUE)
LemonBoyafe04662023-08-23 21:08:11 +02002998 {
2999 rettv->vval.v_number = VVAL_TRUE;
3000 return;
3001 }
3002 }
3003 }
3004 else if (classinfo_tv->v_type == VAR_CLASS)
3005 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003006 rettv->vval.v_number = class_instance_of(object_tv->vval.v_object->obj_class,
3007 classinfo_tv->vval.v_class);
LemonBoyafe04662023-08-23 21:08:11 +02003008 }
3009}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00003010
3011#endif // FEAT_EVAL