blob: e847bf086407463cb740216706b7bdbb55572f06 [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 Lakshmananac773182024-04-27 11:36:12 +0200139 init_arg = skipwhite(init_arg);
Yegappan Lakshmananfe55c312024-04-28 09:54:09 +0200140 if (*init_arg != NUL && !vim9_comment_start(init_arg))
Yegappan Lakshmananac773182024-04-27 11:36:12 +0200141 {
142 semsg(_(e_trailing_characters_str), init_arg);
143 return FAIL;
144 }
145
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +0200146 // No type specified for the member. Set it to "any" and the correct
147 // type will be set when the object is instantiated.
Bram Moolenaard505d172022-12-18 21:42:55 +0000148 if (type == NULL)
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200149 type = &t_any;
Bram Moolenaard505d172022-12-18 21:42:55 +0000150
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200151 *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
152 // Free the memory pointed by expr_start.
Bram Moolenaard505d172022-12-18 21:42:55 +0000153 clear_evalarg(&evalarg, NULL);
154 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200155 else if (!valid_declaration_type(type))
Bram Moolenaard505d172022-12-18 21:42:55 +0000156 return FAIL;
157
158 *type_ret = type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000159 return OK;
160}
161
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100162typedef struct oc_newmember_S oc_newmember_T;
163struct oc_newmember_S
164{
165 garray_T *gap;
166 char_u *varname;
167 char_u *varname_end;
168 int has_public;
169 int has_final;
170 int has_type;
171 type_T *type;
172 char_u *init_expr;
173};
174
Bram Moolenaard505d172022-12-18 21:42:55 +0000175/*
176 * Add a member to an object or a class.
177 * Returns OK when successful, "init_expr" will be consumed then.
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100178 * Returns OK on success and FAIL on memory allocation failure (caller might
179 * need to free "init_expr").
Bram Moolenaard505d172022-12-18 21:42:55 +0000180 */
181 static int
182add_member(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200183 garray_T *gap,
184 char_u *varname,
185 char_u *varname_end,
186 int has_public,
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100187 int has_final,
188 int has_const,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200189 int has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200190 type_T *type,
191 char_u *init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +0000192{
193 if (ga_grow(gap, 1) == FAIL)
194 return FAIL;
195 ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
196 m->ocm_name = vim_strnsave(varname, varname_end - varname);
=?UTF-8?q?Ola=20S=C3=B6der?=d8742472023-03-05 13:12:32 +0000197 m->ocm_access = has_public ? VIM_ACCESS_ALL
198 : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100199 if (has_final)
200 m->ocm_flags |= OCMFLAG_FINAL;
201 if (has_const)
202 m->ocm_flags |= OCMFLAG_CONST;
203 if (has_type)
204 m->ocm_flags |= OCMFLAG_HAS_TYPE;
Bram Moolenaard505d172022-12-18 21:42:55 +0000205 m->ocm_type = type;
206 if (init_expr != NULL)
207 m->ocm_init = init_expr;
208 ++gap->ga_len;
209 return OK;
210}
211
212/*
213 * Move the class or object members found while parsing a class into the class.
214 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000215 * "parent_members" points to the members in the parent class (if any)
216 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000217 * "members" will be set to the newly allocated array of members and
218 * "member_count" set to the number of members.
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100219 * Returns OK on success and FAIL on memory allocation failure.
Bram Moolenaard505d172022-12-18 21:42:55 +0000220 */
221 static int
222add_members_to_class(
223 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000224 ocmember_T *parent_members,
225 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000226 ocmember_T **members,
227 int *member_count)
228{
Bram Moolenaar83677162023-01-08 19:54:10 +0000229 *member_count = parent_count + gap->ga_len;
230 *members = *member_count == 0 ? NULL
231 : ALLOC_MULT(ocmember_T, *member_count);
232 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000233 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000234 for (int i = 0; i < parent_count; ++i)
235 {
236 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000237 ocmember_T *m = *members + i;
238 *m = parent_members[i];
239 m->ocm_name = vim_strsave(m->ocm_name);
240 if (m->ocm_init != NULL)
241 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000242 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000243 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000244 // new members are moved
245 mch_memmove(*members + parent_count,
246 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000247 VIM_CLEAR(gap->ga_data);
248 return OK;
249}
250
251/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000252 * Convert a member index "idx" of interface "itf" to the member index of class
253 * "cl" implementing that interface.
254 */
255 int
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200256object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000257{
Ernie Rael18143d32023-09-04 22:30:41 +0200258 if (idx >= (is_method ? itf->class_obj_method_count
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200259 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000260 {
261 siemsg("index %d out of range for interface %s", idx, itf->class_name);
262 return 0;
263 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200264
265 // If "cl" is the interface or the class that is extended, then the method
266 // index can be used directly and there is no need to search for the method
267 // index in one of the child classes.
268 if (cl == itf)
269 return idx;
270
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200271 itf2class_T *i2c = NULL;
272 int searching = TRUE;
273 int method_offset = 0;
274
Ernie Raelcf138d42023-09-06 20:45:03 +0200275 for (class_T *super = cl; super != NULL && searching;
276 super = super->class_extends)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200277 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200278 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200279 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200280 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
281 {
282 searching = FALSE;
283 break;
284 }
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200285 }
286 if (searching && is_method)
287 // The parent class methods are stored after the current class
288 // methods.
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200289 method_offset += super->class_obj_method_count_child;
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200290 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000291 if (i2c == NULL)
292 {
293 siemsg("class %s not found on interface %s",
294 cl->class_name, itf->class_name);
295 return 0;
296 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +0200297
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200298 // A table follows the i2c for the class
299 int *table = (int *)(i2c + 1);
300 // "method_offset" is 0, if method is in the current class. If method
301 // is in a parent class, then it is non-zero.
302 return table[idx] + method_offset;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000303}
304
305/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200306 * Check whether a class named "extends_name" is present. If the class is
307 * valid, then "extends_clp" is set with the class pointer.
308 * Returns TRUE if the class name "extends_names" is a valid class.
309 */
310 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200311validate_extends_class(
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100312 class_T *cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200313 char_u *extends_name,
314 class_T **extends_clp,
315 int is_class)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200316{
317 typval_T tv;
318 int success = FALSE;
319
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100320 if (STRCMP(cl->class_name, extends_name) == 0)
321 {
322 semsg(_(e_cannot_extend_str), extends_name);
323 return success;
324 }
325
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200326 tv.v_type = VAR_UNKNOWN;
327 if (eval_variable_import(extends_name, &tv) == FAIL)
328 {
329 semsg(_(e_class_name_not_found_str), extends_name);
330 return success;
331 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200332
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200333 if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100334 || (is_class && IS_INTERFACE(tv.vval.v_class))
335 || (!is_class && !IS_INTERFACE(tv.vval.v_class))
336 || (is_class && IS_ENUM(tv.vval.v_class)))
337 {
338 // a class cannot extend an interface
339 // an interface cannot extend a class
340 // a class cannot extend an enum.
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200341 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100342 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200343 else
344 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200345 class_T *extends_cl = tv.vval.v_class;
346 ++extends_cl->class_refcount;
347 *extends_clp = extends_cl;
348 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200349 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200350 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200351
352 return success;
353}
354
355/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200356 * Check method names in the parent class lineage to make sure the access is
357 * the same for overridden methods.
358 */
359 static int
360validate_extends_methods(
361 garray_T *objmethods_gap,
362 class_T *extends_cl)
363{
364 class_T *super = extends_cl;
365 int method_count = objmethods_gap->ga_len;
366 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
367
368 while (super != NULL)
369 {
370 int extends_method_count = super->class_obj_method_count_child;
371 if (extends_method_count == 0)
372 {
373 super = super->class_extends;
374 continue;
375 }
376
377 ufunc_T **extends_methods = super->class_obj_methods;
378
379 for (int i = 0; i < extends_method_count; i++)
380 {
381 char_u *pstr = extends_methods[i]->uf_name;
382 int extends_private = (*pstr == '_');
383 if (extends_private)
384 pstr++;
385
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200386 // When comparing the method names, ignore the access type (public
387 // and private methods are considered the same).
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200388 for (int j = 0; j < method_count; j++)
389 {
390 char_u *qstr = cl_fp[j]->uf_name;
391 int priv_method = (*qstr == '_');
392 if (priv_method)
393 qstr++;
394 if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
395 {
396 // Method access is different between the super class and
397 // the subclass
398 semsg(_(e_method_str_of_class_str_has_different_access),
399 cl_fp[j]->uf_name, super->class_name);
400 return FALSE;
401 }
402 }
403 }
404 super = super->class_extends;
405 }
406
407 return TRUE;
408}
409
410/*
411 * Check whether a object member variable in "objmembers_gap" is a duplicate of
412 * a member in any of the extended parent class lineage. Returns TRUE if there
413 * are no duplicates.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200414 */
415 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200416extends_check_dup_members(
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200417 garray_T *objmembers_gap,
418 class_T *extends_cl)
419{
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200420 int member_count = objmembers_gap->ga_len;
421 if (member_count == 0)
422 return TRUE;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200423
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200424 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
425
426 // Validate each member variable
427 for (int c_i = 0; c_i < member_count; c_i++)
428 {
429 class_T *p_cl = extends_cl;
430 ocmember_T *c_m = members + c_i;
431 char_u *pstr = (*c_m->ocm_name == '_')
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200432 ? c_m->ocm_name + 1 : c_m->ocm_name;
433
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200434 // Check in all the parent classes in the lineage
435 while (p_cl != NULL)
436 {
437 int p_member_count = p_cl->class_obj_member_count;
438 if (p_member_count == 0)
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200439 {
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200440 p_cl = p_cl->class_extends;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200441 continue;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200442 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200443 ocmember_T *p_members = p_cl->class_obj_members;
444
445 // Compare against all the members in the parent class
446 for (int p_i = 0; p_i < p_member_count; p_i++)
447 {
448 ocmember_T *p_m = p_members + p_i;
449 char_u *qstr = (*p_m->ocm_name == '_')
450 ? p_m->ocm_name + 1 : p_m->ocm_name;
451 if (STRCMP(pstr, qstr) == 0)
452 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200453 semsg(_(e_duplicate_variable_str), c_m->ocm_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200454 return FALSE;
455 }
456 }
457
458 p_cl = p_cl->class_extends;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200459 }
460 }
461
462 return TRUE;
463}
464
465/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200466 * Compare the variable type of interface variables in "objmembers_gap" against
467 * the variable in any of the extended super interface lineage. Used to
468 * compare the variable types when extending interfaces. Returns TRUE if the
469 * variable types are the same.
470 */
471 static int
472extends_check_intf_var_type(
473 garray_T *objmembers_gap,
474 class_T *extends_cl)
475{
476 int member_count = objmembers_gap->ga_len;
477 if (member_count == 0)
478 return TRUE;
479
480 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
481
482 // Validate each member variable
483 for (int c_i = 0; c_i < member_count; c_i++)
484 {
485 class_T *p_cl = extends_cl;
486 ocmember_T *c_m = members + c_i;
487 int var_found = FALSE;
488
489 // Check in all the parent classes in the lineage
490 while (p_cl != NULL && !var_found)
491 {
492 int p_member_count = p_cl->class_obj_member_count;
493 if (p_member_count == 0)
494 {
495 p_cl = p_cl->class_extends;
496 continue;
497 }
498 ocmember_T *p_members = p_cl->class_obj_members;
499
500 // Compare against all the members in the parent class
501 for (int p_i = 0; p_i < p_member_count; p_i++)
502 {
503 where_T where = WHERE_INIT;
504 ocmember_T *p_m = p_members + p_i;
505
506 if (STRCMP(p_m->ocm_name, c_m->ocm_name) != 0)
507 continue;
508
509 // Ensure the type is matching.
510 where.wt_func_name = (char *)c_m->ocm_name;
511 where.wt_kind = WT_MEMBER;
512
513 if (check_type(p_m->ocm_type, c_m->ocm_type, TRUE,
514 where) == FAIL)
515 return FALSE;
516
517 var_found = TRUE;
518 }
519
520 p_cl = p_cl->class_extends;
521 }
522 }
523
524 return TRUE;
525}
526
527/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200528 * When extending an abstract class, check whether all the abstract methods in
529 * the parent class are implemented. Returns TRUE if all the methods are
530 * implemented.
531 */
532 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200533validate_abstract_class_methods(
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200534 garray_T *classmethods_gap,
535 garray_T *objmethods_gap,
536 class_T *extends_cl)
537{
538 for (int loop = 1; loop <= 2; ++loop)
539 {
540 // loop == 1: check class methods
541 // loop == 2: check object methods
542 int extends_method_count = loop == 1
543 ? extends_cl->class_class_function_count
544 : extends_cl->class_obj_method_count;
545 if (extends_method_count == 0)
546 continue;
547
548 ufunc_T **extends_methods = loop == 1
549 ? extends_cl->class_class_functions
550 : extends_cl->class_obj_methods;
551
552 int method_count = loop == 1 ? classmethods_gap->ga_len
553 : objmethods_gap->ga_len;
554 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
555 ? classmethods_gap->ga_data
556 : objmethods_gap->ga_data);
557
558 for (int i = 0; i < extends_method_count; i++)
559 {
560 ufunc_T *uf = extends_methods[i];
Yegappan Lakshmanan1db15142023-09-19 20:34:05 +0200561 if (!IS_ABSTRACT_METHOD(uf))
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200562 continue;
563
564 int method_found = FALSE;
565
566 for (int j = 0; j < method_count; j++)
567 {
568 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
569 {
570 method_found = TRUE;
571 break;
572 }
573 }
574
575 if (!method_found)
576 {
577 semsg(_(e_abstract_method_str_not_found), uf->uf_name);
578 return FALSE;
579 }
580 }
581 }
582
583 return TRUE;
584}
585
586/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200587 * Returns TRUE if the interface variable "if_var" is present in the list of
588 * variables in "cl_mt" or in the parent lineage of one of the extended classes
589 * in "extends_cl". For a class variable, 'is_class_var' is TRUE.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200590 */
591 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200592intf_variable_present(
593 char_u *intf_class_name,
594 ocmember_T *if_var,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200595 ocmember_T *cl_mt,
596 int cl_member_count,
597 class_T *extends_cl)
598{
599 int variable_present = FALSE;
600
601 for (int cl_i = 0; cl_i < cl_member_count; ++cl_i)
602 {
603 ocmember_T *m = &cl_mt[cl_i];
604 where_T where = WHERE_INIT;
605
606 if (STRCMP(if_var->ocm_name, m->ocm_name) != 0)
607 continue;
608
609 // Ensure the access type is same
610 if (if_var->ocm_access != m->ocm_access)
611 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200612 semsg(_(e_variable_str_of_interface_str_has_different_access),
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200613 if_var->ocm_name, intf_class_name);
614 return FALSE;
615 }
616
617 // Ensure the type is matching.
618 if (m->ocm_type == &t_any)
619 {
620 // variable type is not specified. Use the variable type in the
621 // interface.
622 m->ocm_type = if_var->ocm_type;
623 }
624 else
625 {
626 where.wt_func_name = (char *)m->ocm_name;
627 where.wt_kind = WT_MEMBER;
628 if (check_type(if_var->ocm_type, m->ocm_type, TRUE,
629 where) == FAIL)
630 return FALSE;
631 }
632
633 variable_present = TRUE;
634 break;
635 }
636
637 if (!variable_present && extends_cl != NULL)
638 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200639 int ext_cl_count = extends_cl->class_obj_member_count;
640 ocmember_T *ext_cl_mt = extends_cl->class_obj_members;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200641 return intf_variable_present(intf_class_name, if_var,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200642 ext_cl_mt, ext_cl_count,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200643 extends_cl->class_extends);
644 }
645
646 return variable_present;
647}
648
649/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200650 * Check the variables of the interface class "ifcl" match object variables
651 * ("objmembers_gap") of a class.
652 * Returns TRUE if the object variables names are valid.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200653 */
654 static int
655validate_interface_variables(
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200656 char_u *intf_class_name,
657 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200658 garray_T *objmembers_gap,
659 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200660{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200661 int if_count = ifcl->class_obj_member_count;
662 if (if_count == 0)
663 return TRUE;
664
665 ocmember_T *if_ms = ifcl->class_obj_members;
666 ocmember_T *cl_ms = (ocmember_T *)(objmembers_gap->ga_data);
667 int cl_count = objmembers_gap->ga_len;
668 for (int if_i = 0; if_i < if_count; ++if_i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200669 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200670 if (!intf_variable_present(intf_class_name, &if_ms[if_i], cl_ms,
671 cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200672 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200673 semsg(_(e_variable_str_of_interface_str_not_implemented),
674 if_ms[if_i].ocm_name, intf_class_name);
675 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200676 }
677 }
678
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200679 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200680}
681
682/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200683 * Returns TRUE if the method signature of "if_method" and "cl_method" matches.
684 */
685 static int
686intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method)
687{
688 where_T where = WHERE_INIT;
689
690 // Ensure the type is matching.
691 where.wt_func_name = (char *)if_method->uf_name;
692 where.wt_kind = WT_METHOD;
693 if (check_type(if_method->uf_func_type, cl_method->uf_func_type, TRUE,
694 where) == FAIL)
695 return FALSE;
696
697 return TRUE;
698}
699
700/*
701 * Returns TRUE if the interface method "if_ufunc" is present in the list of
702 * methods in "cl_fp" or in the parent lineage of one of the extended classes
703 * in "extends_cl". For a class method, 'is_class_method' is TRUE.
704 */
705 static int
706intf_method_present(
707 ufunc_T *if_ufunc,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200708 ufunc_T **cl_fp,
709 int cl_count,
710 class_T *extends_cl)
711{
712 int method_present = FALSE;
713
714 for (int cl_i = 0; cl_i < cl_count; ++cl_i)
715 {
716 char_u *cl_name = cl_fp[cl_i]->uf_name;
717 if (STRCMP(if_ufunc->uf_name, cl_name) == 0)
718 {
719 // Ensure the type is matching.
720 if (!intf_method_type_matches(if_ufunc, cl_fp[cl_i]))
721 return FALSE;
722 method_present = TRUE;
723 break;
724 }
725 }
726
727 if (!method_present && extends_cl != NULL)
728 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200729 ufunc_T **ext_cl_fp = (ufunc_T **)(extends_cl->class_obj_methods);
730 int ext_cl_count = extends_cl->class_obj_method_count;
731 return intf_method_present(if_ufunc, ext_cl_fp, ext_cl_count,
732 extends_cl->class_extends);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200733 }
734
735 return method_present;
736}
737
738/*
739 * Validate that a new class implements all the class/instance methods in the
740 * interface "ifcl". The new class methods are in "classfunctions_gap" and the
741 * new object methods are in "objmemthods_gap". Also validates the method
742 * types.
743 * Returns TRUE if all the interface class/object methods are implemented in
744 * the new class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200745 */
746 static int
747validate_interface_methods(
748 char_u *intf_class_name,
749 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200750 garray_T *objmethods_gap,
751 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200752{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200753 int if_count = ifcl->class_obj_method_count;
754 if (if_count == 0)
755 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200756
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200757 ufunc_T **if_fp = ifcl->class_obj_methods;
758 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
759 int cl_count = objmethods_gap->ga_len;
760 for (int if_i = 0; if_i < if_count; ++if_i)
761 {
762 char_u *if_name = if_fp[if_i]->uf_name;
763
764 if (!intf_method_present(if_fp[if_i], cl_fp, cl_count, extends_cl))
765 {
766 semsg(_(e_method_str_of_interface_str_not_implemented),
767 if_name, intf_class_name);
768 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200769 }
770 }
771
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200772 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200773}
774
775/*
776 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200777 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200778 * object methods and object members in the new class are in
779 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
780 * "objmembers_gap" respectively.
781 */
782 static int
783validate_implements_classes(
784 garray_T *impl_gap,
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +0100785 garray_T *intf_classes_gap,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200786 garray_T *objmethods_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200787 garray_T *objmembers_gap,
788 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200789{
790 int success = TRUE;
791
792 for (int i = 0; i < impl_gap->ga_len && success; ++i)
793 {
794 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
795 typval_T tv;
796 tv.v_type = VAR_UNKNOWN;
797 if (eval_variable_import(impl, &tv) == FAIL)
798 {
799 semsg(_(e_interface_name_not_found_str), impl);
800 success = FALSE;
801 break;
802 }
803
804 if (tv.v_type != VAR_CLASS
805 || tv.vval.v_class == NULL
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100806 || !IS_INTERFACE(tv.vval.v_class))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200807 {
808 semsg(_(e_not_valid_interface_str), impl);
809 success = FALSE;
810 clear_tv(&tv);
811 break;
812 }
813
814 class_T *ifcl = tv.vval.v_class;
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +0100815 if (ga_grow(intf_classes_gap, 1) == FAIL)
816 {
817 success = FALSE;
818 clear_tv(&tv);
819 break;
820 }
821 ((class_T **)intf_classes_gap->ga_data)[intf_classes_gap->ga_len]
822 = ifcl;
823 intf_classes_gap->ga_len++;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200824 ++ifcl->class_refcount;
825
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200826 // check the variables of the interface match the members of the class
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200827 success = validate_interface_variables(impl, ifcl, objmembers_gap,
828 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200829
830 // check the functions/methods of the interface match the
831 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200832 if (success)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200833 success = validate_interface_methods(impl, ifcl, objmethods_gap,
834 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200835 clear_tv(&tv);
836 }
837
838 return success;
839}
840
841/*
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +0100842 * Returns TRUE if the interface class "ifcl" is already present in the
843 * "intf_classes_gap" grow array.
844 */
845 static int
846is_interface_class_present(garray_T *intf_classes_gap, class_T *ifcl)
847{
848 for (int j = 0; j < intf_classes_gap->ga_len; j++)
849 {
850 if (((class_T **)intf_classes_gap)[j] == ifcl)
851 return TRUE;
852 }
853
854 return FALSE;
855}
856
857/*
858 * Add interface "ifcl" from a super class to "intf_classes_gap" and the class
859 * name to "impl_gap".
860 */
861 static int
862add_interface_from_super_class(
863 class_T *ifcl,
864 garray_T *impl_gap,
865 garray_T *intf_classes_gap)
866{
867 char_u *intf_name;
868
869 // Add the interface name to "impl_gap"
870 intf_name = vim_strsave(ifcl->class_name);
871 if (intf_name == NULL)
872 return FALSE;
873
874 if (ga_grow(impl_gap, 1) == FAIL)
875 return FALSE;
876
877 char_u **intf_names = (char_u **)impl_gap->ga_data;
878 intf_names[impl_gap->ga_len] = intf_name;
879 impl_gap->ga_len++;
880
881 // Add the interface class to "intf_classes_gap"
882 if (ga_grow(intf_classes_gap, 1) == FAIL)
883 return FALSE;
884
885 class_T **intf_classes = (class_T **)intf_classes_gap->ga_data;
886 intf_classes[intf_classes_gap->ga_len] = ifcl;
887 intf_classes_gap->ga_len++;
888 ++ifcl->class_refcount;
889
890 return TRUE;
891}
892
893/*
894 * Add "super" class interfaces to "intf_classes_gap" (if not present already)
895 * Add the interface class names to "impl_gap".
896 */
897 static int
898add_super_class_interfaces(
899 class_T *super,
900 garray_T *impl_gap,
901 garray_T *intf_classes_gap)
902{
903 // Iterate through all the interfaces implemented by "super"
904 for (int i = 0; i < super->class_interface_count; i++)
905 {
906 class_T *ifcl = super->class_interfaces_cl[i];
907
908 if (!is_interface_class_present(intf_classes_gap, ifcl))
909 add_interface_from_super_class(ifcl, impl_gap, intf_classes_gap);
910 }
911
912 return TRUE;
913}
914
915/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200916 * Check no function argument name is used as a class member.
917 * (Object members are always accessed with "this." prefix, so no need
918 * to check them.)
919 */
920 static int
921check_func_arg_names(
922 garray_T *classfunctions_gap,
923 garray_T *objmethods_gap,
924 garray_T *classmembers_gap)
925{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200926 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200927 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200928 {
929 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
930
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200931 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200932 {
933 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
934
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200935 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200936 {
937 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
938 garray_T *mgap = classmembers_gap;
939
940 // Check all the class member names
941 for (int mi = 0; mi < mgap->ga_len; ++mi)
942 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200943 char_u *mname =
944 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200945 if (STRCMP(aname, mname) == 0)
946 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200947 if (uf->uf_script_ctx.sc_sid > 0)
948 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
949
950 semsg(_(e_argument_already_declared_in_class_str),
951 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200952
953 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200954 }
955 }
956 }
957 }
958 }
959
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200960 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200961}
962
963/*
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200964 * Returns TRUE if 'varname' is a reserved keyword name
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200965 */
966 static int
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200967is_reserved_varname(char_u *varname, char_u *varname_end)
968{
969 int reserved = FALSE;
970 char_u save_varname_end = *varname_end;
971 *varname_end = NUL;
972
973 reserved = check_reserved_name(varname, FALSE) == FAIL;
974
975 *varname_end = save_varname_end;
976
977 return reserved;
978}
979
980/*
981 * Returns TRUE if the variable "varname" is already defined either as a class
982 * variable or as an object variable.
983 */
984 static int
985is_duplicate_variable(
986 garray_T *class_members,
987 garray_T *obj_members,
988 char_u *varname,
989 char_u *varname_end)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200990{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200991 char_u *name = vim_strnsave(varname, varname_end - varname);
992 char_u *pstr = (*name == '_') ? name + 1 : name;
993 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200994
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200995 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200996 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200997 // loop == 1: class variables, loop == 2: object variables
998 garray_T *vgap = (loop == 1) ? class_members : obj_members;
999 for (int i = 0; i < vgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001000 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001001 ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i;
1002 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1
1003 : m->ocm_name;
1004 if (STRCMP(pstr, qstr) == 0)
1005 {
1006 semsg(_(e_duplicate_variable_str), name);
1007 dup = TRUE;
1008 break;
1009 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001010 }
1011 }
1012
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001013 vim_free(name);
1014 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001015}
1016
1017/*
1018 * Returns TRUE if the method "name" is already defined.
1019 */
1020 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001021is_duplicate_method(
1022 garray_T *classmethods_gap,
1023 garray_T *objmethods_gap,
1024 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001025{
1026 char_u *pstr = (*name == '_') ? name + 1 : name;
1027
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001028 // loop 1: class methods, loop 2: object methods
1029 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001030 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001031 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
1032 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001033 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001034 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
1035 char_u *qstr = *n == '_' ? n + 1 : n;
1036 if (STRCMP(pstr, qstr) == 0)
1037 {
1038 semsg(_(e_duplicate_function_str), name);
1039 return TRUE;
1040 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001041 }
1042 }
1043
1044 return FALSE;
1045}
1046
1047/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001048 * Returns TRUE if the constructor is valid.
1049 */
1050 static int
1051is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
1052{
1053 // Constructors are not allowed in abstract classes.
1054 if (is_abstract)
1055 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001056 emsg(_(e_cannot_define_new_method_in_abstract_class));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001057 return FALSE;
1058 }
1059 // A constructor is always static, no need to define it so.
1060 if (has_static)
1061 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001062 emsg(_(e_cannot_define_new_method_as_static));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001063 return FALSE;
1064 }
1065 // A return type should not be specified for the new()
1066 // constructor method.
1067 if (uf->uf_ret_type->tt_type != VAR_VOID)
1068 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001069 emsg(_(e_cannot_use_a_return_type_with_new_method));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001070 return FALSE;
1071 }
1072 return TRUE;
1073}
1074
1075/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001076 * Returns TRUE if 'uf' is a supported builtin method and has the correct
1077 * method signature.
1078 */
1079 static int
1080object_check_builtin_method_sig(ufunc_T *uf)
1081{
1082 char_u *name = uf->uf_name;
1083 int valid = FALSE;
1084 type_T method_sig;
1085 type_T method_rt;
1086 where_T where = WHERE_INIT;
1087
1088 // validate the method signature
1089 CLEAR_FIELD(method_sig);
1090 CLEAR_FIELD(method_rt);
1091 method_sig.tt_type = VAR_FUNC;
1092
1093 if (STRCMP(name, "len") == 0)
1094 {
1095 // def __len(): number
1096 method_rt.tt_type = VAR_NUMBER;
1097 method_sig.tt_member = &method_rt;
1098 valid = TRUE;
1099 }
1100 else if (STRCMP(name, "empty") == 0)
1101 {
1102 // def __empty(): bool
1103 method_rt.tt_type = VAR_BOOL;
1104 method_sig.tt_member = &method_rt;
1105 valid = TRUE;
1106 }
1107 else if (STRCMP(name, "string") == 0)
1108 {
1109 // def __string(): string
1110 method_rt.tt_type = VAR_STRING;
1111 method_sig.tt_member = &method_rt;
1112 valid = TRUE;
1113 }
1114 else
1115 semsg(_(e_builtin_object_method_str_not_supported), uf->uf_name);
1116
1117 where.wt_func_name = (char *)uf->uf_name;
1118 where.wt_kind = WT_METHOD;
1119 if (valid && !check_type(&method_sig, uf->uf_func_type, TRUE, where))
1120 valid = FALSE;
1121
1122 return valid;
1123}
1124
1125/*
1126 * Returns TRUE if "funcname" is a supported builtin object method name
1127 */
1128 int
1129is_valid_builtin_obj_methodname(char_u *funcname)
1130{
1131 switch (funcname[0])
1132 {
1133 case 'e':
1134 return STRNCMP(funcname, "empty", 5) == 0;
1135
1136 case 'l':
1137 return STRNCMP(funcname, "len", 3) == 0;
1138
1139 case 'n':
1140 return STRNCMP(funcname, "new", 3) == 0;
1141
1142 case 's':
1143 return STRNCMP(funcname, "string", 6) == 0;
1144 }
1145
1146 return FALSE;
1147}
1148
1149
1150/*
1151 * Returns the builtin method "name" in object "obj". Returns NULL if the
1152 * method is not found.
1153 */
1154 ufunc_T *
1155class_get_builtin_method(
1156 class_T *cl,
1157 class_builtin_T builtin_method,
1158 int *method_idx)
1159{
1160 *method_idx = -1;
1161
1162 if (cl == NULL)
1163 return NULL;
1164
1165 *method_idx = cl->class_builtin_methods[builtin_method];
1166 return *method_idx != -1 ? cl->class_obj_methods[*method_idx] : NULL;
1167}
1168
1169/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001170 * Update the interface class lookup table for the member index on the
1171 * interface to the member index in the class implementing the interface.
1172 * And a lookup table for the object method index on the interface
1173 * to the object method index in the class implementing the interface.
1174 * This is also used for updating the lookup table for the extended class
1175 * hierarchy.
1176 */
1177 static int
1178update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001179 class_T *ifcl,
1180 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001181 garray_T *objmethods,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001182 int pobj_method_offset)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001183{
1184 if (ifcl == NULL)
1185 return OK;
1186
1187 // Table for members.
1188 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001189 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001190 if (if2cl == NULL)
1191 return FAIL;
1192 if2cl->i2c_next = ifcl->class_itf2class;
1193 ifcl->class_itf2class = if2cl;
1194 if2cl->i2c_class = cl;
1195 if2cl->i2c_is_method = FALSE;
1196
1197 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
1198 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
1199 {
1200 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001201 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001202 {
1203 int *table = (int *)(if2cl + 1);
1204 table[if_i] = cl_i;
1205 break;
1206 }
1207 }
1208
1209 // Table for methods.
1210 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001211 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001212 if (if2cl == NULL)
1213 return FAIL;
1214 if2cl->i2c_next = ifcl->class_itf2class;
1215 ifcl->class_itf2class = if2cl;
1216 if2cl->i2c_class = cl;
1217 if2cl->i2c_is_method = TRUE;
1218
1219 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
1220 {
1221 int done = FALSE;
1222 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
1223 {
1224 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001225 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001226 {
1227 int *table = (int *)(if2cl + 1);
1228 table[if_i] = cl_i;
1229 done = TRUE;
1230 break;
1231 }
1232 }
1233
1234 // extended class object method is not overridden by the child class.
1235 // Keep the method declared in one of the parent classes in the
1236 // lineage.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001237 if (!done)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001238 {
1239 // If "ifcl" is not the immediate parent of "cl", then search in
1240 // the intermediate parent classes.
1241 if (cl->class_extends != ifcl)
1242 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001243 class_T *parent = cl->class_extends;
1244 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001245
1246 while (!done && parent != NULL && parent != ifcl)
1247 {
1248
1249 for (int cl_i = 0;
1250 cl_i < parent->class_obj_method_count_child; ++cl_i)
1251 {
1252 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
1253 parent->class_obj_methods[cl_i]->uf_name)
1254 == 0)
1255 {
1256 int *table = (int *)(if2cl + 1);
1257 table[if_i] = method_offset + cl_i;
1258 done = TRUE;
1259 break;
1260 }
1261 }
1262 method_offset += parent->class_obj_method_count_child;
1263 parent = parent->class_extends;
1264 }
1265 }
1266
1267 if (!done)
1268 {
1269 int *table = (int *)(if2cl + 1);
1270 table[if_i] = pobj_method_offset + if_i;
1271 }
1272 }
1273 }
1274
1275 return OK;
1276}
1277
1278/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001279 * Update the member and object method lookup tables for a new class in the
1280 * interface class.
1281 * For each interface add a lookup table for the member index on the interface
1282 * to the member index in the new class. And a lookup table for the object
1283 * method index on the interface to the object method index in the new class.
1284 */
1285 static int
1286add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
1287{
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001288 // update the lookup table for all the implemented interfaces
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001289 for (int i = 0; i < cl->class_interface_count; ++i)
1290 {
1291 class_T *ifcl = cl->class_interfaces_cl[i];
1292
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001293 // update the lookup table for this interface and all its super
1294 // interfaces.
1295 while (ifcl != NULL)
1296 {
1297 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
1298 0) == FAIL)
1299 return FAIL;
1300 ifcl = ifcl->class_extends;
1301 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001302 }
1303
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001304 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001305 if (extends_cl != NULL)
1306 {
1307 class_T *pclass = extends_cl;
1308 int pobj_method_offset = objmethods_gap->ga_len;
1309
1310 // Update the entire lineage of extended classes.
1311 while (pclass != NULL)
1312 {
1313 if (update_member_method_lookup_table(pclass, cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001314 objmethods_gap, pobj_method_offset) == FAIL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001315 return FAIL;
1316
1317 pobj_method_offset += pclass->class_obj_method_count_child;
1318 pclass = pclass->class_extends;
1319 }
1320 }
1321
1322 return OK;
1323}
1324
1325/*
1326 * Add class members to a new class. Allocate a typval for each class member
1327 * and initialize it.
1328 */
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001329 static int
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001330add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001331{
1332 // Allocate a typval for each class member and initialize it.
1333 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
1334 cl->class_class_member_count);
1335 if (cl->class_members_tv == NULL)
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001336 return FAIL;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001337
1338 for (int i = 0; i < cl->class_class_member_count; ++i)
1339 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001340 ocmember_T *m = &cl->class_class_members[i];
1341 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001342 if (m->ocm_init != NULL)
1343 {
1344 typval_T *etv = eval_expr(m->ocm_init, eap);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001345 if (etv == NULL)
1346 return FAIL;
1347
1348 if (m->ocm_type->tt_type == VAR_ANY
1349 && !(m->ocm_flags & OCMFLAG_HAS_TYPE)
1350 && etv->v_type != VAR_SPECIAL)
1351 // If the member variable type is not yet set, then use
1352 // the initialization expression type.
1353 m->ocm_type = typval2type(etv, get_copyID(),
1354 type_list_gap,
1355 TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
1356 *tv = *etv;
1357 vim_free(etv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001358 }
1359 else
1360 {
1361 // TODO: proper default value
1362 tv->v_type = m->ocm_type->tt_type;
1363 tv->vval.v_string = NULL;
1364 }
LemonBoyf4af3312024-07-04 13:43:12 +02001365 set_tv_type(tv, m->ocm_type);
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001366 if (m->ocm_flags & OCMFLAG_CONST)
1367 item_lock(tv, DICT_MAXNEST, TRUE, TRUE);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001368 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001369
1370 return OK;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001371}
1372
1373/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001374 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001375 */
1376 static void
1377add_default_constructor(
1378 class_T *cl,
1379 garray_T *classfunctions_gap,
1380 garray_T *type_list_gap)
1381{
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001382 garray_T fga;
1383 int is_enum = IS_ENUM(cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001384
1385 ga_init2(&fga, 1, 1000);
1386 ga_concat(&fga, (char_u *)"new(");
1387 for (int i = 0; i < cl->class_obj_member_count; ++i)
1388 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001389 if (i < 2 && is_enum)
1390 // The first two object variables in an enum are the enum value
1391 // name and ordinal. Don't initialize these object variables in
1392 // the default constructor as they are already initialized right
1393 // after creating the object.
1394 continue;
1395
1396 if (i > (is_enum ? 2 : 0))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001397 ga_concat(&fga, (char_u *)", ");
1398 ga_concat(&fga, (char_u *)"this.");
1399 ocmember_T *m = cl->class_obj_members + i;
1400 ga_concat(&fga, (char_u *)m->ocm_name);
1401 ga_concat(&fga, (char_u *)" = v:none");
1402 }
1403 ga_concat(&fga, (char_u *)")\nenddef\n");
1404 ga_append(&fga, NUL);
1405
1406 exarg_T fea;
1407 CLEAR_FIELD(fea);
1408 fea.cmdidx = CMD_def;
1409 fea.cmd = fea.arg = fga.ga_data;
1410
1411 garray_T lines_to_free;
1412 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1413
h-eastb895b0f2023-09-24 15:46:31 +02001414 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
1415 cl->class_obj_members, cl->class_obj_member_count);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001416
1417 ga_clear_strings(&lines_to_free);
1418 vim_free(fga.ga_data);
1419
1420 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1421 {
1422 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001423 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001424 ++classfunctions_gap->ga_len;
1425
1426 nf->uf_flags |= FC_NEW;
1427 nf->uf_ret_type = get_type_ptr(type_list_gap);
1428 if (nf->uf_ret_type != NULL)
1429 {
1430 nf->uf_ret_type->tt_type = VAR_OBJECT;
1431 nf->uf_ret_type->tt_class = cl;
1432 nf->uf_ret_type->tt_argcount = 0;
1433 nf->uf_ret_type->tt_args = NULL;
1434 }
1435 }
1436}
1437
1438/*
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001439 * Add the class methods and object methods to the new class "cl".
1440 * When extending a class "extends_cl", add the instance methods from the
1441 * parent class also.
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001442 * Returns OK on success and FAIL on memory allocation failure.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001443 */
1444 static int
1445add_classfuncs_objmethods(
1446 class_T *cl,
1447 class_T *extends_cl,
1448 garray_T *classfunctions_gap,
1449 garray_T *objmethods_gap)
1450{
1451 // loop 1: class functions, loop 2: object methods
1452 for (int loop = 1; loop <= 2; ++loop)
1453 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001454 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1455 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001456 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001457 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001458 : &cl->class_obj_methods;
1459
1460 int parent_count = 0;
1461 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001462 // Include object methods from the parent.
1463 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001464 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001465 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001466 : extends_cl->class_obj_method_count;
1467
1468 *fcount = parent_count + gap->ga_len;
1469 if (*fcount == 0)
1470 {
1471 *fup = NULL;
1472 continue;
1473 }
1474 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1475 if (*fup == NULL)
1476 return FAIL;
1477
1478 if (gap->ga_len != 0)
1479 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001480 VIM_CLEAR(gap->ga_data);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001481 if (loop == 1)
1482 cl->class_class_function_count_child = gap->ga_len;
1483 else
1484 cl->class_obj_method_count_child = gap->ga_len;
1485
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001486 if (loop == 2)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001487 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001488 // Copy instance methods from the parent.
1489
1490 for (int i = 0; i < parent_count; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001491 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001492 // Can't use the same parent function, because "uf_class" is
1493 // different and compilation will have a different result.
1494 // Put them after the functions in the current class, object
1495 // methods may be overruled, then "super.Method()" is used to
1496 // find a method from the parent.
1497 ufunc_T *pf = (extends_cl->class_obj_methods)[i];
1498 (*fup)[gap->ga_len + i] = copy_function(pf);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001499
1500 // If the child class overrides a function from the parent
1501 // the signature must be equal.
1502 char_u *pname = pf->uf_name;
1503 for (int ci = 0; ci < gap->ga_len; ++ci)
1504 {
1505 ufunc_T *cf = (*fup)[ci];
1506 char_u *cname = cf->uf_name;
1507 if (STRCMP(pname, cname) == 0)
1508 {
1509 where_T where = WHERE_INIT;
1510 where.wt_func_name = (char *)pname;
1511 where.wt_kind = WT_METHOD;
1512 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1513 TRUE, where);
1514 }
1515 }
1516 }
1517 }
1518
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001519 // Set the class pointer on all the functions and object methods.
1520 for (int i = 0; i < *fcount; ++i)
1521 {
1522 ufunc_T *fp = (*fup)[i];
1523 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001524 if (i < gap->ga_len)
1525 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001526 if (loop == 2)
1527 fp->uf_flags |= FC_OBJECT;
1528 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001529
1530 ga_clear(gap);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001531 }
1532
1533 return OK;
1534}
1535
1536/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001537 * Update the index of object methods called by builtin functions.
1538 */
1539 static void
1540update_builtin_method_index(class_T *cl)
1541{
1542 int i;
1543
1544 for (i = 0; i < CLASS_BUILTIN_MAX; i++)
1545 cl->class_builtin_methods[i] = -1;
1546
1547 for (i = 0; i < cl->class_obj_method_count; i++)
1548 {
1549 ufunc_T *uf = cl->class_obj_methods[i];
1550
1551 if (cl->class_builtin_methods[CLASS_BUILTIN_STRING] == -1
1552 && STRCMP(uf->uf_name, "string") == 0)
1553 cl->class_builtin_methods[CLASS_BUILTIN_STRING] = i;
1554 else if (cl->class_builtin_methods[CLASS_BUILTIN_EMPTY] == -1 &&
1555 STRCMP(uf->uf_name, "empty") == 0)
1556 cl->class_builtin_methods[CLASS_BUILTIN_EMPTY] = i;
1557 else if (cl->class_builtin_methods[CLASS_BUILTIN_LEN] == -1 &&
1558 STRCMP(uf->uf_name, "len") == 0)
1559 cl->class_builtin_methods[CLASS_BUILTIN_LEN] = i;
1560 }
1561}
1562
1563/*
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001564 * Return the end of the class name starting at "arg". Valid characters in a
1565 * class name are alphanumeric characters and "_". Also handles imported class
1566 * names.
1567 */
1568 static char_u *
1569find_class_name_end(char_u *arg)
1570{
1571 char_u *end = arg;
1572
1573 while (ASCII_ISALNUM(*end) || *end == '_'
1574 || (*end == '.' && (ASCII_ISALNUM(end[1]) || end[1] == '_')))
1575 ++end;
1576
1577 return end;
1578}
1579
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001580/*
1581 * Returns TRUE if the enum value "varname" is already defined.
1582 */
1583 static int
1584is_duplicate_enum(
1585 garray_T *enum_gap,
1586 char_u *varname,
1587 char_u *varname_end)
1588{
1589 char_u *name = vim_strnsave(varname, varname_end - varname);
1590 int dup = FALSE;
1591
1592 for (int i = 0; i < enum_gap->ga_len; ++i)
1593 {
1594 ocmember_T *m = ((ocmember_T *)enum_gap->ga_data) + i;
1595 if (STRCMP(name, m->ocm_name) == 0)
1596 {
1597 semsg(_(e_duplicate_enum_str), name);
1598 dup = TRUE;
1599 break;
1600 }
1601 }
1602
1603 vim_free(name);
1604 return dup;
1605}
1606
1607/*
1608 * Parse the enum values in "line" separated by comma and add them to "gap".
1609 * If the last enum value is found, then "enum_end" is set to TRUE.
1610 */
1611 static int
1612enum_parse_values(
1613 exarg_T *eap,
1614 class_T *en,
1615 char_u *line,
1616 garray_T *gap,
1617 int *num_enum_values,
1618 int *enum_end)
1619{
1620 evalarg_T evalarg;
1621 char_u *p = line;
1622 char initexpr_buf[1024];
1623 char_u last_char = NUL;
1624 int rc = OK;
1625
1626 fill_evalarg_from_eap(&evalarg, eap, FALSE);
1627
1628 int did_emsg_before = did_emsg;
1629 while (*p != NUL)
1630 {
1631 // ignore comment
1632 if (*p == '#')
1633 break;
1634
1635 if (!eval_isnamec1(*p))
1636 {
1637 semsg(_(e_invalid_enum_value_declaration_str), p);
1638 break;
1639 }
1640
1641 char_u *eni_name_start = p;
1642 char_u *eni_name_end = to_name_end(p, FALSE);
1643
1644 if (is_duplicate_enum(gap, eni_name_start, eni_name_end))
1645 break;
1646
1647 p = skipwhite(eni_name_end);
1648
1649 char_u *init_expr = NULL;
1650 if (*p == '(')
1651 {
1652 if (VIM_ISWHITE(p[-1]))
1653 {
1654 semsg(_(e_no_white_space_allowed_before_str_str), "(", line);
1655 break;
1656 }
1657
1658 char_u *expr_start, *expr_end;
1659
1660 p = eni_name_start;
1661 (void)skip_expr_concatenate(&p, &expr_start, &expr_end, &evalarg);
1662
1663 while (*expr_start && *expr_start != '(')
1664 expr_start++;
1665
1666 if (expr_end > expr_start)
1667 init_expr = vim_strnsave(expr_start, expr_end - expr_start);
1668 }
1669
1670 if (init_expr == NULL)
1671 vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new()",
1672 en->class_name);
1673 else
1674 {
1675 vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new%s",
1676 en->class_name, init_expr);
1677 vim_free(init_expr);
1678 }
1679 if (add_member(gap, eni_name_start, eni_name_end, FALSE,
1680 TRUE, TRUE, TRUE, &en->class_object_type,
1681 vim_strsave((char_u *)initexpr_buf)) == FAIL)
1682 break;
1683
1684 ++*num_enum_values;
1685
1686 if (*p != '#')
1687 last_char = *p;
1688
1689 if (*p != NUL && *p != ',')
1690 break;
1691
1692 if (*p == ',')
1693 {
1694 if (!IS_WHITE_OR_NUL(p[1]))
1695 {
1696 semsg(_(e_white_space_required_after_str_str), ",", line);
1697 break;
1698 }
1699 if (VIM_ISWHITE(p[-1]))
1700 {
1701 semsg(_(e_no_white_space_allowed_before_str_str), ",", line);
1702 break;
1703 }
1704 p = skipwhite(p + 1);
1705 }
1706 }
1707
Doug Kearnsdbe39ed2025-01-04 17:12:24 +01001708 p = skipwhite(p);
1709
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001710 if (*p != NUL && *p != '#')
1711 {
1712 if (did_emsg == did_emsg_before)
1713 semsg(_(e_missing_comma_before_argument_str), p);
1714 rc = FAIL;
1715 }
1716
1717 if (last_char != ',')
1718 // last enum value should not be terminated by ","
1719 *enum_end = TRUE;
1720
1721 // Free the memory pointed by expr_start.
1722 clear_evalarg(&evalarg, NULL);
1723
1724 return rc;
1725}
1726
1727/*
1728 * Add the "values" class variable (List of enum value objects) to the enum
1729 * class "en"
1730 */
1731 static int
1732enum_add_values_member(
1733 class_T *en,
1734 garray_T *gap,
1735 int num_enum_values,
1736 garray_T *type_list_gap)
1737{
1738 garray_T fga;
1739 int rc = FAIL;
1740
1741 ga_init2(&fga, 1, 1000);
1742 ga_concat(&fga, (char_u *)"[");
1743 for (int i = 0; i < num_enum_values; ++i)
1744 {
1745 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
1746
1747 if (i > 0)
1748 ga_concat(&fga, (char_u *)", ");
1749 ga_concat(&fga, en->class_name);
1750 ga_concat(&fga, (char_u *)".");
1751 ga_concat(&fga, (char_u *)m->ocm_name);
1752 }
1753 ga_concat(&fga, (char_u *)"]");
1754 ga_append(&fga, NUL);
1755
1756 char_u *varname = (char_u *)"values";
1757
1758 type_T *type = get_type_ptr(type_list_gap);
1759 if (type == NULL)
1760 goto done;
1761
1762 type->tt_type = VAR_LIST;
1763 type->tt_member = get_type_ptr(type_list_gap);
1764 if (type->tt_member != NULL)
1765 {
1766 type->tt_member->tt_type = VAR_OBJECT;
1767 type->tt_member->tt_class = en;
1768 }
1769
1770 rc = add_member(gap, varname, varname + 6, FALSE, FALSE, TRUE, TRUE, type,
1771 vim_strsave((char_u *)fga.ga_data));
1772
1773done:
1774 vim_free(fga.ga_data);
1775
1776 return rc;
1777}
1778
1779/*
1780 * Clear the constructor method names in a enum class, so that an enum class
1781 * cannot be instantiated.
1782 */
1783 static void
1784enum_clear_constructors(class_T *en)
1785{
1786 for (int i = 0; i < en->class_class_function_count; ++i)
1787 {
1788 ufunc_T *fp = en->class_class_functions[i];
1789
1790 if (fp->uf_flags & FC_NEW)
1791 *fp->uf_name = NUL;
1792 }
1793}
1794
1795/*
1796 * Initialize the name and ordinal object variable in the enum value "enval" in
1797 * the enum "en". These values are set during the enum value object creation.
1798 */
1799 void
1800enum_set_internal_obj_vars(class_T *en, object_T *enval)
1801{
1802 int i;
1803
1804 for (i = 0; i < en->class_class_member_count; ++i)
1805 {
1806 typval_T *en_tv = en->class_members_tv + i;
1807 if (en_tv != NULL && en_tv->v_type == VAR_UNKNOWN)
1808 break;
1809 }
1810
1811 // First object variable is the name
1812 ocmember_T *value_ocm = en->class_class_members + i;
1813 typval_T *name_tv = (typval_T *)(enval + 1);
1814 name_tv->v_type = VAR_STRING;
1815 name_tv->vval.v_string = vim_strsave(value_ocm->ocm_name);
1816
1817 // Second object variable is the ordinal
1818 typval_T *ord_tv = (typval_T *)(name_tv + 1);
1819 ord_tv->v_type = VAR_NUMBER;
1820 ord_tv->vval.v_number = i;
1821}
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001822
1823/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001824 * Handle ":class" and ":abstract class" up to ":endclass".
h-eastaa979c72025-01-03 10:19:45 +01001825 * Handle ":enum" up to ":endenum".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001826 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001827 */
1828 void
1829ex_class(exarg_T *eap)
1830{
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001831 int is_class = eap->cmdidx == CMD_class;
1832 int is_abstract = eap->cmdidx == CMD_abstract;
1833 int is_enum = eap->cmdidx == CMD_enum;
1834 int is_interface;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001835 long start_lnum = SOURCING_LNUM;
1836 char_u *arg = eap->arg;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001837
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001838 if (is_abstract)
1839 {
1840 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1841 {
1842 semsg(_(e_invalid_argument_str), arg);
1843 return;
1844 }
1845 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001846 is_class = TRUE;
1847 }
1848
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001849 is_interface = !is_class && !is_enum;
1850
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001851 if (!current_script_is_vim9()
1852 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001853 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001854 {
1855 if (is_class)
1856 emsg(_(e_class_can_only_be_defined_in_vim9_script));
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001857 else if (is_enum)
1858 emsg(_(e_enum_can_only_be_defined_in_vim9_script));
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001859 else
1860 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1861 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001862 }
1863
1864 if (!ASCII_ISUPPER(*arg))
1865 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001866 if (is_class)
1867 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001868 else if (is_enum)
1869 semsg(_(e_enum_name_must_start_with_uppercase_letter_str), arg);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001870 else
1871 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1872 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001873 return;
1874 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001875 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1876 if (!IS_WHITE_OR_NUL(*name_end))
1877 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001878 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001879 return;
1880 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001881 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001882
1883 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001884 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001885
Bram Moolenaar83677162023-01-08 19:54:10 +00001886 // Name for "extends BaseClass"
1887 char_u *extends = NULL;
1888
Bram Moolenaar94674f22023-01-06 18:42:20 +00001889 // Names for "implements SomeInterface"
1890 garray_T ga_impl;
1891 ga_init2(&ga_impl, sizeof(char_u *), 5);
1892
1893 arg = skipwhite(name_end);
1894 while (*arg != NUL && *arg != '#' && *arg != '\n')
1895 {
1896 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001897 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001898 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1899 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001900 if (is_enum)
1901 {
1902 emsg(_(e_enum_cannot_extend_class));
1903 goto early_ret;
1904 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001905 if (extends != NULL)
1906 {
1907 emsg(_(e_duplicate_extends));
1908 goto early_ret;
1909 }
1910 arg = skipwhite(arg + 7);
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001911
1912 char_u *end = find_class_name_end(arg);
Bram Moolenaar83677162023-01-08 19:54:10 +00001913 if (!IS_WHITE_OR_NUL(*end))
1914 {
1915 semsg(_(e_white_space_required_after_name_str), arg);
1916 goto early_ret;
1917 }
1918 extends = vim_strnsave(arg, end - arg);
1919 if (extends == NULL)
1920 goto early_ret;
1921
1922 arg = skipwhite(end + 1);
1923 }
1924 else if (STRNCMP(arg, "implements", 10) == 0
1925 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001926 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001927 if (is_interface)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001928 {
1929 emsg(_(e_interface_cannot_use_implements));
1930 goto early_ret;
1931 }
1932
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001933 if (ga_impl.ga_len > 0)
1934 {
1935 emsg(_(e_duplicate_implements));
1936 goto early_ret;
1937 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001938 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001939
1940 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001941 {
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001942 char_u *impl_end = find_class_name_end(arg);
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001943 if ((!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1944 || (*impl_end == ','
1945 && !IS_WHITE_OR_NUL(*(impl_end + 1))))
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001946 {
1947 semsg(_(e_white_space_required_after_name_str), arg);
1948 goto early_ret;
1949 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001950 if (impl_end - arg == 0)
1951 {
1952 emsg(_(e_missing_name_after_implements));
1953 goto early_ret;
1954 }
1955
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001956 char_u *iname = vim_strnsave(arg, impl_end - arg);
1957 if (iname == NULL)
1958 goto early_ret;
1959 for (int i = 0; i < ga_impl.ga_len; ++i)
1960 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1961 {
1962 semsg(_(e_duplicate_interface_after_implements_str),
1963 iname);
1964 vim_free(iname);
1965 goto early_ret;
1966 }
1967 if (ga_add_string(&ga_impl, iname) == FAIL)
1968 {
1969 vim_free(iname);
1970 goto early_ret;
1971 }
1972 if (*impl_end != ',')
1973 {
1974 arg = skipwhite(impl_end);
1975 break;
1976 }
1977 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001978 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001979 }
1980 else
1981 {
1982 semsg(_(e_trailing_characters_str), arg);
1983early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001984 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001985 ga_clear_strings(&ga_impl);
1986 return;
1987 }
1988 }
1989
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001990 garray_T type_list; // list of pointers to allocated types
1991 ga_init2(&type_list, sizeof(type_T *), 10);
1992
Bram Moolenaard505d172022-12-18 21:42:55 +00001993 // Growarray with class members declared in the class.
1994 garray_T classmembers;
1995 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1996
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001997 // Growarray with functions declared in the class.
1998 garray_T classfunctions;
1999 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00002000
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002001 // Growarray with object members declared in the class.
2002 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00002003 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002004
2005 // Growarray with object methods declared in the class.
2006 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002007 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002008
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002009 class_T *cl = NULL;
2010 class_T *extends_cl = NULL; // class from "extends" argument
2011 class_T **intf_classes = NULL;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002012 int num_enum_values = 0;
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002013
2014 cl = ALLOC_CLEAR_ONE(class_T);
2015 if (cl == NULL)
2016 goto cleanup;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002017
2018 if (is_enum)
2019 cl->class_flags = CLASS_ENUM;
2020 else if (is_interface)
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002021 cl->class_flags = CLASS_INTERFACE;
2022 else if (is_abstract)
2023 cl->class_flags = CLASS_ABSTRACT;
2024
2025 cl->class_refcount = 1;
2026 cl->class_name = vim_strnsave(name_start, name_end - name_start);
2027 if (cl->class_name == NULL)
2028 goto cleanup;
2029
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002030 cl->class_type.tt_type = VAR_CLASS;
2031 cl->class_type.tt_class = cl;
2032 cl->class_object_type.tt_type = VAR_OBJECT;
2033 cl->class_object_type.tt_class = cl;
2034
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002035 // Add the class to the script-local variables.
2036 // TODO: handle other context, e.g. in a function
2037 // TODO: does uf_hash need to be cleared?
2038 typval_T tv;
2039 tv.v_type = VAR_CLASS;
2040 tv.vval.v_class = cl;
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002041 SOURCING_LNUM = start_lnum;
2042 int rc = set_var_const(cl->class_name, current_sctx.sc_sid,
2043 NULL, &tv, FALSE, 0, 0);
2044 if (rc == FAIL)
2045 goto cleanup;
2046
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002047 if (is_enum)
2048 {
2049 // All the enum classes have the name and ordinal object variables.
2050 char_u *varname = (char_u *)"name";
2051 if (add_member(&objmembers, varname, varname + 4, FALSE, FALSE, TRUE,
2052 TRUE, &t_string, NULL) == FAIL)
2053 goto cleanup;
2054
2055 varname = (char_u *)"ordinal";
2056 if (add_member(&objmembers, varname, varname + 7, FALSE, FALSE, TRUE,
2057 TRUE, &t_number, NULL) == FAIL)
2058 goto cleanup;
2059 }
2060
2061 // "export class" gets used when creating the class, don't use "is_export"
2062 // for the items inside the class.
2063 is_export = FALSE;
2064
2065 // When parsing an enum definition, this denotes whether all the enumerated
2066 // values are parsed or not.
2067 int enum_end = FALSE;
2068
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002069 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00002070 * Go over the body of the class/interface until "endclass" or
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002071 * "endinterface" or "endenum" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002072 */
2073 char_u *theline = NULL;
2074 int success = FALSE;
2075 for (;;)
2076 {
2077 vim_free(theline);
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002078 theline = eap->ea_getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002079 if (theline == NULL)
2080 break;
2081 char_u *line = skipwhite(theline);
2082
Bram Moolenaar418b5472022-12-20 13:38:22 +00002083 // Skip empty and comment lines.
2084 if (*line == NUL)
2085 continue;
2086 if (*line == '#')
2087 {
2088 if (vim9_bad_comment(line))
2089 break;
2090 continue;
2091 }
2092
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002093 char_u *p = line;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002094
2095 char *end_name;
2096 int shortlen;
2097 int fullen;
2098 if (is_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002099 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002100 end_name = "endclass";
2101 shortlen = 4;
2102 fullen = 8;
2103 }
2104 else if (is_enum)
2105 {
2106 end_name = "endenum";
2107 shortlen = 4;
2108 fullen = 7;
2109 }
2110 else
2111 {
2112 end_name = "endinterface";
2113 shortlen = 5;
2114 fullen = 12;
2115 }
2116
2117 if (checkforcmd(&p, end_name, shortlen))
2118 {
2119 if (STRNCMP(line, end_name, fullen) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002120 semsg(_(e_command_cannot_be_shortened_str), line);
2121 else if (*p == '|' || !ends_excmd2(line, p))
2122 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00002123 else
2124 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002125 break;
2126 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002127
2128 int wrong_endname = FALSE;
2129 if (is_class)
2130 wrong_endname = checkforcmd(&p, "endinterface", 5)
2131 || checkforcmd(&p, "endenum", 4);
2132 else if (is_enum)
2133 wrong_endname = checkforcmd(&p, "endclass", 4)
2134 || checkforcmd(&p, "endinterface", 5);
2135 else
2136 wrong_endname = checkforcmd(&p, "endclass", 4)
2137 || checkforcmd(&p, "endenum", 4);
2138 if (wrong_endname)
Bram Moolenaar554d0312023-01-05 19:59:18 +00002139 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00002140 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00002141 break;
2142 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002143
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002144 if (is_enum && !enum_end)
2145 {
2146 // In an enum, all the enumerated values are at the beginning
2147 // separated by comma. The class and object variables/methods
2148 // follow the values.
2149 if (enum_parse_values(eap, cl, line, &classmembers,
2150 &num_enum_values, &enum_end) == FAIL)
2151 break;
Yegappan Lakshmananabedca92024-03-29 10:08:23 +01002152
2153 if (enum_end)
2154 // Add the enum "values" class variable.
2155 enum_add_values_member(cl, &classmembers, num_enum_values,
2156 &type_list);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002157 continue;
2158 }
2159
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002160 int has_public = FALSE;
2161 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002162 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002163 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002164 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002165 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002166 break;
2167 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002168 if (is_interface)
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002169 {
Yegappan Lakshmananb90e3bc2023-09-28 23:06:48 +02002170 emsg(_(e_public_variable_not_supported_in_interface));
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002171 break;
2172 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002173 has_public = TRUE;
2174 p = skipwhite(line + 6);
2175
Yegappan Lakshmananc51578f2024-04-13 17:58:09 +02002176 if (STRNCMP(p, "def", 3) == 0)
2177 {
2178 emsg(_(e_public_keyword_not_supported_for_method));
2179 break;
2180 }
2181
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002182 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "static", 6) != 0
2183 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002184 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002185 emsg(_(e_public_must_be_followed_by_var_static_final_or_const));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002186 break;
2187 }
2188 }
Bram Moolenaard505d172022-12-18 21:42:55 +00002189
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002190 int abstract_method = FALSE;
2191 char_u *pa = p;
2192 if (checkforcmd(&p, "abstract", 3))
2193 {
2194 if (STRNCMP(pa, "abstract", 8) != 0)
2195 {
2196 semsg(_(e_command_cannot_be_shortened_str), pa);
2197 break;
2198 }
2199
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002200 if (is_enum)
2201 {
2202 // "abstract" not supported in an enum
2203 emsg(_(e_abstract_cannot_be_used_in_enum));
2204 break;
2205 }
2206
2207 if (is_interface)
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01002208 {
2209 // "abstract" not supported in an interface
2210 emsg(_(e_abstract_cannot_be_used_in_interface));
2211 break;
2212 }
2213
2214 if (!is_abstract)
2215 {
2216 semsg(_(e_abstract_method_in_concrete_class), pa);
2217 break;
2218 }
2219
Yegappan Lakshmanan5a539252023-11-04 09:42:46 +01002220 p = skipwhite(pa + 8);
2221 if (STRNCMP(p, "def", 3) != 0)
2222 {
2223 emsg(_(e_abstract_must_be_followed_by_def));
2224 break;
2225 }
2226
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01002227 abstract_method = TRUE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002228 }
2229
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002230 int has_static = FALSE;
2231 char_u *ps = p;
2232 if (checkforcmd(&p, "static", 4))
2233 {
2234 if (STRNCMP(ps, "static", 6) != 0)
2235 {
2236 semsg(_(e_command_cannot_be_shortened_str), ps);
2237 break;
2238 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002239
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002240 if (is_interface)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002241 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002242 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002243 break;
2244 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002245 has_static = TRUE;
2246 p = skipwhite(ps + 6);
Doug Kearns74da0ee2023-12-14 20:26:26 +01002247
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002248 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "def", 3) != 0
2249 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Doug Kearns74da0ee2023-12-14 20:26:26 +01002250 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002251 emsg(_(e_static_must_be_followed_by_var_def_final_or_const));
Doug Kearns74da0ee2023-12-14 20:26:26 +01002252 break;
2253 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002254 }
2255
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002256 int has_final = FALSE;
2257 int has_var = FALSE;
2258 int has_const = FALSE;
2259 if (checkforcmd(&p, "var", 3))
2260 has_var = TRUE;
2261 else if (checkforcmd(&p, "final", 5))
2262 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002263 if (is_interface)
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002264 {
2265 emsg(_(e_final_variable_not_supported_in_interface));
2266 break;
2267 }
2268 has_final = TRUE;
2269 }
2270 else if (checkforcmd(&p, "const", 5))
2271 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002272 if (is_interface)
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002273 {
2274 emsg(_(e_const_variable_not_supported_in_interface));
2275 break;
2276 }
2277 has_const = TRUE;
2278 }
2279 p = skipwhite(p);
2280
Bram Moolenaard505d172022-12-18 21:42:55 +00002281 // object members (public, read access, private):
Doug Kearns74da0ee2023-12-14 20:26:26 +01002282 // "var _varname"
2283 // "var varname"
2284 // "public var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002285 // "final _varname"
2286 // "final varname"
2287 // "public final varname"
2288 // "const _varname"
2289 // "const varname"
2290 // "public const varname"
Doug Kearns74da0ee2023-12-14 20:26:26 +01002291 // class members (public, read access, private):
2292 // "static var _varname"
2293 // "static var varname"
2294 // "public static var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002295 // "static final _varname"
2296 // "static final varname"
2297 // "public static final varname"
2298 // "static const _varname"
2299 // "static const varname"
2300 // "public static const varname"
2301 if (has_var || has_final || has_const)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002302 {
Doug Kearns74da0ee2023-12-14 20:26:26 +01002303 char_u *varname = p;
Bram Moolenaard505d172022-12-18 21:42:55 +00002304 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00002305 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00002306 char_u *init_expr = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002307 int has_type = FALSE;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002308
Doug Kearns74da0ee2023-12-14 20:26:26 +01002309 if (!eval_isnamec1(*p))
2310 {
2311 if (has_static)
2312 semsg(_(e_invalid_class_variable_declaration_str), line);
2313 else
2314 semsg(_(e_invalid_object_variable_declaration_str), line);
2315 break;
2316 }
2317
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002318 if (is_interface && *varname == '_')
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002319 {
2320 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01002321 semsg(_(e_protected_variable_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002322 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002323 break;
2324 }
2325
Bram Moolenaard505d172022-12-18 21:42:55 +00002326 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002327 &varname_end, &has_type, &type_list, &type,
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002328 !is_interface ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002329 break;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002330
2331 if (is_reserved_varname(varname, varname_end)
2332 || is_duplicate_variable(&classmembers, &objmembers,
2333 varname, varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02002334 {
2335 vim_free(init_expr);
2336 break;
2337 }
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002338 if (add_member(has_static ? &classmembers : &objmembers, varname,
2339 varname_end, has_public, has_final, has_const,
2340 has_type, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00002341 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002342 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002343 break;
2344 }
Bram Moolenaard505d172022-12-18 21:42:55 +00002345 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002346
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002347 // constructors:
2348 // def new()
2349 // enddef
2350 // def newOther()
2351 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002352 // object methods and class functions:
2353 // def SomeMethod()
2354 // enddef
2355 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002356 // enddef
2357 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002358 // def <Tval> someMethod()
2359 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002360 else if (checkforcmd(&p, "def", 3))
2361 {
2362 exarg_T ea;
2363 garray_T lines_to_free;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002364 int is_new = STRNCMP(p, "new", 3) == 0;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002365
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002366 if (has_public)
2367 {
2368 // "public" keyword is not supported when defining an object or
2369 // class method
2370 emsg(_(e_public_keyword_not_supported_for_method));
2371 break;
2372 }
2373
2374 if (*p == NUL)
2375 {
2376 // No method name following def
2377 semsg(_(e_not_valid_command_in_class_str), line);
2378 break;
2379 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002380
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002381 if (is_interface && *p == '_')
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01002382 {
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002383 // private methods are not supported in an interface
2384 semsg(_(e_protected_method_not_supported_in_interface), p);
2385 break;
2386 }
2387
2388 if (has_static && !is_new && SAFE_islower(*p) &&
2389 is_valid_builtin_obj_methodname(p))
2390 {
2391 semsg(_(e_builtin_class_method_not_supported), p);
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01002392 break;
2393 }
2394
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002395 CLEAR_FIELD(ea);
2396 ea.cmd = line;
2397 ea.arg = p;
2398 ea.cmdidx = CMD_def;
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002399 ea.ea_getline = eap->ea_getline;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002400 ea.cookie = eap->cookie;
2401
2402 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002403 int class_flags;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002404 if (is_interface)
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002405 class_flags = CF_INTERFACE;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002406 else
2407 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
Bram Moolenaar554d0312023-01-05 19:59:18 +00002408 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02002409 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002410 ga_clear_strings(&lines_to_free);
2411
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002412 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002413 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002414 char_u *name = uf->uf_name;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02002415
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002416 if (is_new && !is_valid_constructor(uf, is_abstract,
2417 has_static))
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002418 {
2419 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01002420 semsg(_(e_protected_method_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002421 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002422 func_clear_free(uf, FALSE);
2423 break;
2424 }
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002425
2426 // check for builtin method
2427 if (!is_new && SAFE_islower(*name) &&
2428 !object_check_builtin_method_sig(uf))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00002429 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02002430 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00002431 break;
2432 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02002433
Bram Moolenaar58b40092023-01-11 15:59:05 +00002434 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002435 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002436 {
2437 success = FALSE;
2438 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02002439 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002440 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00002441
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002442 garray_T *fgap = has_static || is_new
2443 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002444 if (ga_grow(fgap, 1) == OK)
2445 {
2446 if (is_new)
2447 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002448
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002449 if (abstract_method)
2450 uf->uf_flags |= FC_ABSTRACT;
2451
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002452 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
2453 ++fgap->ga_len;
2454 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002455 }
2456 }
2457
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002458 else
2459 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00002460 if (is_class)
2461 semsg(_(e_not_valid_command_in_class_str), line);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002462 else if (is_enum)
2463 semsg(_(e_not_valid_command_in_enum_str), line);
Bram Moolenaar554d0312023-01-05 19:59:18 +00002464 else
2465 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002466 break;
2467 }
2468 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002469
2470 if (theline == NULL && !success && is_enum)
2471 emsg(_(e_missing_endenum));
2472
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002473 vim_free(theline);
2474
Yegappan Lakshmananabedca92024-03-29 10:08:23 +01002475 if (success && is_enum && num_enum_values == 0)
2476 // Empty enum statement. Add an empty "values" class variable
2477 enum_add_values_member(cl, &classmembers, 0, &type_list);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002478
Bram Moolenaar83677162023-01-08 19:54:10 +00002479 /*
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002480 * Check a few things
Bram Moolenaar83677162023-01-08 19:54:10 +00002481 */
2482
2483 // Check the "extends" class is valid.
2484 if (success && extends != NULL)
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002485 success = validate_extends_class(cl, extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00002486 VIM_CLEAR(extends);
2487
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002488 // Check the new object methods to make sure their access (public or
2489 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002490 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002491 success = validate_extends_methods(&objmethods, extends_cl);
2492
2493 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002494 // variables in the extended class lineage. If an interface is extending
2495 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002496 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002497 {
2498 if (is_class)
2499 success = extends_check_dup_members(&objmembers, extends_cl);
2500 else
2501 success = extends_check_intf_var_type(&objmembers, extends_cl);
2502 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002503
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002504 // When extending an abstract class, make sure all the abstract methods in
2505 // the parent class are implemented. If the current class is an abstract
2506 // class, then there is no need for this check.
2507 if (success && !is_abstract && extends_cl != NULL
2508 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002509 success = validate_abstract_class_methods(&classfunctions,
2510 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002511
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +01002512 // Process the "implements" entries
Bram Moolenaar83677162023-01-08 19:54:10 +00002513 // Check all "implements" entries are valid.
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +01002514 garray_T intf_classes_ga;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002515
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +01002516 ga_init2(&intf_classes_ga, sizeof(class_T *), 5);
2517
2518 if (success && ga_impl.ga_len > 0)
2519 success = validate_implements_classes(&ga_impl, &intf_classes_ga,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002520 &objmethods, &objmembers, extends_cl);
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +01002521
2522 // inherit the super class interfaces
2523 if (success && extends_cl != NULL)
2524 success = add_super_class_interfaces(extends_cl, &ga_impl,
2525 &intf_classes_ga);
2526
2527 intf_classes = intf_classes_ga.ga_data;
2528 intf_classes_ga.ga_len = 0;
Bram Moolenaar94674f22023-01-06 18:42:20 +00002529
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002530 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00002531 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002532 success = check_func_arg_names(&classfunctions, &objmethods,
2533 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00002534
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002535 if (success)
2536 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002537 // "endclass" or "endinterface" or "endenum" encountered without any
2538 // failures
Bram Moolenaard505d172022-12-18 21:42:55 +00002539
Bram Moolenaard0200c82023-01-28 15:19:40 +00002540 if (extends_cl != NULL)
2541 {
2542 cl->class_extends = extends_cl;
2543 extends_cl->class_flags |= CLASS_EXTENDED;
2544 }
Bram Moolenaar83677162023-01-08 19:54:10 +00002545
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002546 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00002547 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002548 NULL,
2549 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00002550 &cl->class_class_members,
2551 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00002552 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00002553 extends_cl == NULL ? NULL
2554 : extends_cl->class_obj_members,
2555 extends_cl == NULL ? 0
2556 : extends_cl->class_obj_member_count,
2557 &cl->class_obj_members,
2558 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002559 goto cleanup;
2560
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00002561 if (ga_impl.ga_len > 0)
2562 {
2563 // Move the "implements" names into the class.
2564 cl->class_interface_count = ga_impl.ga_len;
2565 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
2566 if (cl->class_interfaces == NULL)
2567 goto cleanup;
2568 for (int i = 0; i < ga_impl.ga_len; ++i)
2569 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
2570 VIM_CLEAR(ga_impl.ga_data);
2571 ga_impl.ga_len = 0;
2572
Bram Moolenaard0200c82023-01-28 15:19:40 +00002573 cl->class_interfaces_cl = intf_classes;
2574 intf_classes = NULL;
2575 }
2576
2577 if (cl->class_interface_count > 0 || extends_cl != NULL)
2578 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002579 // Add a method and member lookup table to each of the interface
2580 // classes.
2581 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
2582 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00002583 }
2584
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002585 int have_new = FALSE;
2586 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002587 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002588 {
2589 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
2590 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002591 {
2592 have_new = TRUE;
2593 break;
2594 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002595 }
2596
2597 if (have_new)
2598 // The return type of new() is an object of class "cl"
2599 class_func->uf_ret_type->tt_class = cl;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002600 else if ((is_class || is_enum) && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002601 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002602 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002603
Bram Moolenaar58b40092023-01-11 15:59:05 +00002604 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002605 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
2606 &objmethods) == FAIL)
2607 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002608
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002609 update_builtin_method_index(cl);
2610
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002611 class_created(cl);
2612
2613 // Allocate a typval for each class member and initialize it.
2614 if ((is_class || is_enum) && cl->class_class_member_count > 0)
2615 if (add_class_members(cl, eap, &type_list) == FAIL)
2616 goto cleanup;
2617
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002618 cl->class_type_list = type_list;
2619
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002620 if (is_enum)
2621 {
2622 // clear the constructor method names, so that an enum class cannot
2623 // be instantiated
2624 enum_clear_constructors(cl);
2625 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002626
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002627 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00002628 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002629
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002630 return;
2631 }
2632
2633cleanup:
Bram Moolenaar83677162023-01-08 19:54:10 +00002634 vim_free(extends);
2635 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002636
2637 if (intf_classes != NULL)
2638 {
2639 for (int i = 0; i < ga_impl.ga_len; ++i)
2640 class_unref(intf_classes[i]);
2641 vim_free(intf_classes);
2642 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00002643 ga_clear_strings(&ga_impl);
2644
Bram Moolenaard505d172022-12-18 21:42:55 +00002645 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002646 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002647 garray_T *gap = round == 1 ? &classmembers : &objmembers;
2648 if (gap->ga_len == 0 || gap->ga_data == NULL)
2649 continue;
2650
2651 for (int i = 0; i < gap->ga_len; ++i)
2652 {
2653 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2654 vim_free(m->ocm_name);
2655 vim_free(m->ocm_init);
2656 }
2657 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002658 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002659
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002660 for (int i = 0; i < objmethods.ga_len; ++i)
2661 {
2662 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2663 func_clear_free(uf, FALSE);
2664 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002665 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002666
2667 for (int i = 0; i < classfunctions.ga_len; ++i)
2668 {
2669 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2670 func_clear_free(uf, FALSE);
2671 }
2672 ga_clear(&classfunctions);
2673
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002674 clear_type_list(&type_list);
2675}
2676
2677/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002678 * Find member "name" in class "cl", set "member_idx" to the member index and
2679 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002680 * When "is_object" is TRUE, then look for object members. Otherwise look for
2681 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002682 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002683 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002684 */
2685 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002686oc_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002687 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002688 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002689 char_u *name,
2690 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002691 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002692{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002693 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002694 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002695
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002696 *member_idx = -1; // not found (yet)
2697
2698 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2699 member_idx);
2700 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002701 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002702 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2703 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002704 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002705 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002706
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002707 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002708}
2709
2710/*
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002711 * Given a class or object variable index, return the variable type
2712 */
2713 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002714oc_member_type_by_idx(
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002715 class_T *cl,
2716 int is_object,
2717 int member_idx)
2718{
2719 ocmember_T *m;
2720 int member_count;
2721
2722 if (is_object)
2723 {
2724 m = cl->class_obj_members;
2725 member_count = cl->class_obj_member_count;
2726 }
2727 else
2728 {
2729 m = cl->class_class_members;
2730 member_count = cl->class_class_member_count;
2731 }
2732
2733 if (member_idx >= member_count)
2734 return NULL;
2735
2736 return m[member_idx].ocm_type;
2737}
2738
2739/*
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002740 * Type aliases (:type)
2741 */
2742
Yegappan Lakshmanana04003a2024-10-27 21:54:11 +01002743 static void
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002744typealias_free(typealias_T *ta)
2745{
2746 // ta->ta_type is freed in clear_type_list()
2747 vim_free(ta->ta_name);
2748 vim_free(ta);
2749}
2750
2751 void
2752typealias_unref(typealias_T *ta)
2753{
2754 if (ta != NULL && --ta->ta_refcount <= 0)
2755 typealias_free(ta);
2756}
2757
2758/*
2759 * Handle ":type". Create an alias for a type specification.
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002760 */
2761 void
Dominique Pellé0268ff32024-07-28 21:12:20 +02002762ex_type(exarg_T *eap)
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002763{
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002764 char_u *arg = eap->arg;
2765
2766 if (!current_script_is_vim9()
2767 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002768 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002769 {
2770 emsg(_(e_type_can_only_be_defined_in_vim9_script));
2771 return;
2772 }
2773
2774 if (*arg == NUL)
2775 {
2776 emsg(_(e_missing_typealias_name));
2777 return;
2778 }
2779
2780 if (!ASCII_ISUPPER(*arg))
2781 {
2782 semsg(_(e_type_name_must_start_with_uppercase_letter_str), arg);
2783 return;
2784 }
2785
2786 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
2787 if (!IS_WHITE_OR_NUL(*name_end))
2788 {
2789 semsg(_(e_white_space_required_after_name_str), arg);
2790 return;
2791 }
2792 char_u *name_start = arg;
2793
2794 arg = skipwhite(name_end);
2795 if (*arg != '=')
2796 {
2797 semsg(_(e_missing_equal_str), arg);
2798 return;
2799 }
2800 if (!IS_WHITE_OR_NUL(*(arg + 1)))
2801 {
2802 semsg(_(e_white_space_required_after_str_str), "=", arg);
2803 return;
2804 }
2805 arg++;
2806 arg = skipwhite(arg);
2807
2808 if (*arg == NUL)
2809 {
2810 emsg(_(e_missing_typealias_type));
2811 return;
2812 }
2813
2814 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
2815 type_T *type = parse_type(&arg, &si->sn_type_list, TRUE);
2816 if (type == NULL)
2817 return;
2818
2819 if (*arg != NUL)
2820 {
2821 // some text after the type
2822 semsg(_(e_trailing_characters_str), arg);
2823 return;
2824 }
2825
2826 int cc = *name_end;
2827 *name_end = NUL;
2828
2829 typval_T tv;
2830 tv.v_type = VAR_UNKNOWN;
2831 if (eval_variable_import(name_start, &tv) == OK)
2832 {
2833 if (tv.v_type == VAR_TYPEALIAS)
2834 semsg(_(e_typealias_already_exists_for_str), name_start);
2835 else
2836 semsg(_(e_redefining_script_item_str), name_start);
2837 clear_tv(&tv);
2838 goto done;
2839 }
2840
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02002841 // Create a script-local variable for the type alias.
2842 if (type->tt_type != VAR_OBJECT)
2843 {
2844 tv.v_type = VAR_TYPEALIAS;
2845 tv.v_lock = 0;
2846 tv.vval.v_typealias = ALLOC_CLEAR_ONE(typealias_T);
2847 ++tv.vval.v_typealias->ta_refcount;
2848 tv.vval.v_typealias->ta_name = vim_strsave(name_start);
2849 tv.vval.v_typealias->ta_type = type;
2850 }
2851 else
2852 {
2853 // When creating a type alias for a class, use the class type itself to
2854 // create the type alias variable. This is needed to use the type
2855 // alias to invoke class methods (e.g. new()) and use class variables.
2856 tv.v_type = VAR_CLASS;
2857 tv.v_lock = 0;
2858 tv.vval.v_class = type->tt_class;
2859 ++tv.vval.v_class->class_refcount;
2860 }
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002861 set_var_const(name_start, current_sctx.sc_sid, NULL, &tv, FALSE,
2862 ASSIGN_CONST | ASSIGN_FINAL, 0);
2863
2864done:
2865 *name_end = cc;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002866}
2867
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002868/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002869 * Returns OK if a member variable named "name" is present in the class "cl".
2870 * Otherwise returns FAIL. If found, the member variable typval is set in
2871 * "rettv". If "is_object" is TRUE, then the object member variable table is
2872 * searched. Otherwise the class member variable table is searched.
2873 */
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002874 int
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002875get_member_tv(
2876 class_T *cl,
2877 int is_object,
2878 char_u *name,
2879 size_t namelen,
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002880 class_T *current_class,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002881 typval_T *rettv)
2882{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002883 ocmember_T *m;
2884 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002885
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002886 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2887 &m_idx);
2888 if (m == NULL)
2889 return FAIL;
2890
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002891 if (*name == '_' && (current_class == NULL ||
2892 !class_instance_of(current_class, cl)))
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002893 {
Ernie Rael03042a22023-11-11 08:53:32 +01002894 emsg_var_cl_define(e_cannot_access_protected_variable_str,
Ernie Raele6c9aa52023-10-06 19:55:52 +02002895 m->ocm_name, 0, cl);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002896 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002897 }
2898
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002899 if (is_object)
2900 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002901 // The object only contains a pointer to the class, the member values
2902 // array follows right after that.
2903 object_T *obj = rettv->vval.v_object;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002904 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2905 copy_tv(tv, rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002906 object_unref(obj);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002907 }
2908 else
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002909 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002910 copy_tv(&cl->class_members_tv[m_idx], rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002911 class_unref(cl);
2912 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002913
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002914 return OK;
2915}
2916
2917/*
2918 * Call an object or class method "name" in class "cl". The method return
2919 * value is returned in "rettv".
2920 */
2921 static int
2922call_oc_method(
2923 class_T *cl,
2924 char_u *name,
2925 size_t len,
2926 char_u *name_end,
2927 evalarg_T *evalarg,
2928 char_u **arg,
2929 typval_T *rettv)
2930{
2931 ufunc_T *fp;
2932 typval_T argvars[MAX_FUNC_ARGS + 1];
2933 int argcount = 0;
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002934 ocmember_T *ocm = NULL;
2935 int m_idx;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002936
2937 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
2938 if (fp == NULL)
2939 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002940 // could be an object or class funcref variable
2941 ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
2942 if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
2943 {
2944 method_not_found_msg(cl, rettv->v_type, name, len);
2945 return FAIL;
2946 }
2947
Yegappan Lakshmanan3e336502024-04-04 19:35:59 +02002948 if (*name == '_')
2949 {
2950 // Protected object or class funcref variable
2951 semsg(_(e_cannot_access_protected_variable_str), ocm->ocm_name,
2952 cl->class_name);
2953 return FAIL;
2954 }
2955
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002956 if (rettv->v_type == VAR_OBJECT)
2957 {
2958 // funcref object variable
2959 object_T *obj = rettv->vval.v_object;
2960 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2961 copy_tv(tv, rettv);
2962 }
2963 else
2964 // funcref class variable
2965 copy_tv(&cl->class_members_tv[m_idx], rettv);
2966 *arg = name_end;
2967 return OK;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002968 }
2969
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002970 if (ocm == NULL && *fp->uf_name == '_')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002971 {
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002972 // Cannot access a protected method outside of a class
Ernie Rael03042a22023-11-11 08:53:32 +01002973 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002974 return FAIL;
2975 }
2976
2977 char_u *argp = name_end;
Ernie Raelb077b582023-12-14 20:11:44 +01002978 int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount, FALSE);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002979 if (ret == FAIL)
2980 return FAIL;
2981
2982 funcexe_T funcexe;
2983 CLEAR_FIELD(funcexe);
2984 funcexe.fe_evaluate = TRUE;
2985 if (rettv->v_type == VAR_OBJECT)
2986 {
2987 funcexe.fe_object = rettv->vval.v_object;
2988 ++funcexe.fe_object->obj_refcount;
2989 }
2990
2991 // Clear the class or object after calling the function, in
2992 // case the refcount is one.
2993 typval_T tv_tofree = *rettv;
2994 rettv->v_type = VAR_UNKNOWN;
2995
2996 // Call the user function. Result goes into rettv;
2997 int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
2998 NULL);
2999
3000 // Clear the previous rettv and the arguments.
3001 clear_tv(&tv_tofree);
3002 for (int idx = 0; idx < argcount; ++idx)
3003 clear_tv(&argvars[idx]);
3004
3005 if (error != FCERR_NONE)
3006 {
3007 user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
3008 return FAIL;
3009 }
3010 *arg = argp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003011
3012 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003013}
3014
3015/*
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003016 * Create a partial typval for "obj.obj_method" and store it in "rettv".
3017 * Returns OK on success and FAIL on memory allocation failure.
3018 */
3019 int
3020obj_method_to_partial_tv(object_T *obj, ufunc_T *obj_method, typval_T *rettv)
3021{
3022 partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
3023 if (pt == NULL)
3024 return FAIL;
3025
3026 pt->pt_refcount = 1;
3027 if (obj != NULL)
3028 {
3029 pt->pt_obj = obj;
3030 ++pt->pt_obj->obj_refcount;
3031 }
3032 pt->pt_auto = TRUE;
3033 pt->pt_func = obj_method;
3034 func_ptr_ref(pt->pt_func);
3035
3036 rettv->v_type = VAR_PARTIAL;
3037 rettv->vval.v_partial = pt;
3038
3039 return OK;
3040}
3041
3042/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003043 * Evaluate what comes after a class:
3044 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003045 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003046 * - class constructor: SomeClass.new()
3047 * - object member: someObject.varname
3048 * - object method: someObject.SomeMethod()
3049 *
3050 * "*arg" points to the '.'.
3051 * "*arg" is advanced to after the member name or method call.
3052 *
3053 * Returns FAIL or OK.
3054 */
3055 int
3056class_object_index(
3057 char_u **arg,
3058 typval_T *rettv,
3059 evalarg_T *evalarg,
3060 int verbose UNUSED) // give error messages
3061{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003062 if (VIM_ISWHITE((*arg)[1]))
3063 {
3064 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
3065 return FAIL;
3066 }
3067
3068 ++*arg;
3069 char_u *name = *arg;
3070 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
3071 if (name_end == name)
3072 return FAIL;
3073 size_t len = name_end - name;
3074
Ernie Raeld615a312023-10-05 20:28:16 +02003075 int did_emsg_save = did_emsg;
Bram Moolenaar552bdca2023-02-17 21:08:50 +00003076 class_T *cl;
3077 if (rettv->v_type == VAR_CLASS)
3078 cl = rettv->vval.v_class;
3079 else // VAR_OBJECT
3080 {
3081 if (rettv->vval.v_object == NULL)
3082 {
3083 emsg(_(e_using_null_object));
3084 return FAIL;
3085 }
3086 cl = rettv->vval.v_object->obj_class;
3087 }
3088
Bram Moolenaard13dd302023-03-11 20:56:35 +00003089 if (cl == NULL)
3090 {
3091 emsg(_(e_incomplete_type));
3092 return FAIL;
3093 }
3094
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003095 if (*name_end == '(')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003096 // Invoke the class or object method
3097 return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003098
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003099 else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003100 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003101 // Search in the object member variable table and the class member
3102 // variable table.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003103 int is_object = rettv->v_type == VAR_OBJECT;
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003104 if (get_member_tv(cl, is_object, name, len, NULL, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003105 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003106 *arg = name_end;
3107 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003108 }
3109
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003110 // could be a class method or an object method
3111 int fidx;
3112 ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
3113 if (fp != NULL)
3114 {
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003115 // Protected methods are not accessible outside the class
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003116 if (*name == '_')
3117 {
Ernie Rael03042a22023-11-11 08:53:32 +01003118 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003119 return FAIL;
3120 }
3121
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003122 if (obj_method_to_partial_tv(is_object ? rettv->vval.v_object :
3123 NULL, fp, rettv) == FAIL)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003124 return FAIL;
3125
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003126 *arg = name_end;
3127 return OK;
3128 }
3129
Ernie Raeld615a312023-10-05 20:28:16 +02003130 if (did_emsg == did_emsg_save)
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +02003131 member_not_found_msg(cl, rettv->v_type, name, len);
Bram Moolenaard505d172022-12-18 21:42:55 +00003132 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003133
3134 return FAIL;
3135}
3136
3137/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003138 * If "arg" points to a class or object method, return it.
3139 * Otherwise return NULL.
3140 */
3141 ufunc_T *
3142find_class_func(char_u **arg)
3143{
3144 char_u *name = *arg;
3145 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
3146 if (name_end == name || *name_end != '.')
3147 return NULL;
3148
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003149 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003150 size_t len = name_end - name;
3151 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003152 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00003153 if (eval_variable(name, (int)len,
3154 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003155 return NULL;
3156 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00003157 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003158
3159 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
3160 : tv.vval.v_object->obj_class;
3161 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00003162 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003163 char_u *fname = name_end + 1;
3164 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
3165 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00003166 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003167 len = fname_end - fname;
3168
Ernie Rael4d00b832023-09-11 19:54:42 +02003169 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003170
Bram Moolenaareb533502022-12-14 15:06:11 +00003171fail_after_eval:
3172 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003173 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003174}
3175
3176/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003177 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003178 * Returns -1, if the variable is not found.
3179 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003180 */
3181 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003182class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003183{
Ernie Rael4d00b832023-09-11 19:54:42 +02003184 int idx;
3185 class_member_lookup(cl, name, namelen, &idx);
3186 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003187}
3188
3189/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003190 * Returns a pointer to the class member variable "name" in the class "cl".
3191 * Returns NULL if the variable is not found.
3192 * The member variable index is set in "idx".
3193 */
3194 ocmember_T *
3195class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3196{
Ernie Rael4d00b832023-09-11 19:54:42 +02003197 ocmember_T *ret_m = NULL;
3198 int ret_idx = -1;
3199 for (int i = 0; i < cl->class_class_member_count; ++i)
3200 {
3201 ocmember_T *m = &cl->class_class_members[i];
3202 if (namelen)
3203 {
3204 if (STRNCMP(name, m->ocm_name, namelen) == 0
3205 && m->ocm_name[namelen] == NUL)
3206 {
3207 ret_m = m;
3208 ret_idx = i;
3209 break;
3210 }
3211 }
3212 else if (STRCMP(name, m->ocm_name) == 0)
3213 {
3214 ret_m = m;
3215 ret_idx = i;
zeertzjqd9be94c2024-07-14 10:20:20 +02003216 break;
Ernie Rael4d00b832023-09-11 19:54:42 +02003217 }
3218 }
3219 if (idx != NULL)
3220 *idx = ret_idx;
3221 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003222}
3223
3224/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003225 * Returns a pointer to the class method "name" in class "cl".
3226 * Returns NULL if the method is not found.
3227 * The method index is set in "idx".
3228 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003229 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003230class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3231{
Ernie Rael4d00b832023-09-11 19:54:42 +02003232 ufunc_T *ret_fp = NULL;
3233 int ret_idx = -1;
3234 for (int i = 0; i < cl->class_class_function_count; ++i)
3235 {
3236 ufunc_T *fp = cl->class_class_functions[i];
3237 char_u *ufname = (char_u *)fp->uf_name;
3238 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
3239 {
3240 ret_fp = fp;
3241 ret_idx = i;
3242 break;
3243 }
3244 }
3245 if (idx != NULL)
3246 *idx = ret_idx;
3247 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003248}
3249
3250/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003251 * Returns the index of class method "name" in the class "cl".
3252 * Returns -1, if the method is not found.
3253 */
3254 int
3255class_method_idx(class_T *cl, char_u *name, size_t namelen)
3256{
3257 int idx;
3258 class_method_lookup(cl, name, namelen, &idx);
3259 return idx;
3260}
3261
3262/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003263 * Returns the index of object member variable "name" in the class "cl".
3264 * Returns -1, if the variable is not found.
3265 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
3266 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003267 static int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003268object_member_idx(class_T *cl, char_u *name, size_t namelen)
3269{
Ernie Rael4d00b832023-09-11 19:54:42 +02003270 int idx;
3271 object_member_lookup(cl, name, namelen, &idx);
3272 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02003273}
3274
3275/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003276 * Returns a pointer to the object member variable "name" in the class "cl".
3277 * Returns NULL if the variable is not found.
3278 * The object member variable index is set in "idx".
3279 */
3280 ocmember_T *
3281object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3282{
Ernie Rael4d00b832023-09-11 19:54:42 +02003283 ocmember_T *ret_m = NULL;
3284 int ret_idx = -1;
3285 for (int i = 0; i < cl->class_obj_member_count; ++i)
3286 {
3287 ocmember_T *m = &cl->class_obj_members[i];
3288 if (namelen)
3289 {
3290 if (STRNCMP(name, m->ocm_name, namelen) == 0
3291 && m->ocm_name[namelen] == NUL)
3292 {
3293 ret_m = m;
3294 ret_idx = i;
3295 break;
3296 }
3297 }
3298 else if (STRCMP(name, m->ocm_name) == 0)
zeertzjqd9be94c2024-07-14 10:20:20 +02003299 {
Ernie Rael4d00b832023-09-11 19:54:42 +02003300 ret_m = m;
3301 ret_idx = i;
zeertzjqd9be94c2024-07-14 10:20:20 +02003302 break;
3303 }
Ernie Rael4d00b832023-09-11 19:54:42 +02003304 }
3305 if (idx != NULL)
3306 *idx = ret_idx;
3307 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003308}
3309
3310/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003311 * Returns a pointer to the object method "name" in class "cl".
3312 * Returns NULL if the method is not found.
3313 * The object method index is set in "idx".
3314 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003315 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003316object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3317{
Ernie Rael4d00b832023-09-11 19:54:42 +02003318 ufunc_T *ret_fp = NULL;
3319 int ret_idx = -1;
3320 for (int i = 0; i < cl->class_obj_method_count; ++i)
3321 {
3322 ufunc_T *fp = cl->class_obj_methods[i];
3323 // Use a separate pointer to avoid that ASAN complains about
3324 // uf_name[] only being 4 characters.
3325 char_u *ufname = (char_u *)fp->uf_name;
3326 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
3327 {
3328 ret_fp = fp;
3329 ret_idx = i;
3330 break;
3331 }
3332 }
3333 if (idx != NULL)
3334 *idx = ret_idx;
3335 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003336}
3337
3338/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003339 * Returns the index of object method "name" in the class "cl".
3340 * Returns -1, if the method is not found.
3341 */
3342 int
3343object_method_idx(class_T *cl, char_u *name, size_t namelen)
3344{
3345 int idx;
3346 object_method_lookup(cl, name, namelen, &idx);
3347 return idx;
3348}
3349
3350/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003351 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
3352 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
3353 * object member variable.
3354 *
3355 * Returns a pointer to the member variable structure if variable is found.
3356 * Otherwise returns NULL. The member variable index is set in "*idx".
3357 */
3358 ocmember_T *
3359member_lookup(
3360 class_T *cl,
3361 vartype_T v_type,
3362 char_u *name,
3363 size_t namelen,
3364 int *idx)
3365{
3366 if (v_type == VAR_CLASS)
3367 return class_member_lookup(cl, name, namelen, idx);
3368 else
3369 return object_member_lookup(cl, name, namelen, idx);
3370}
3371
3372/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003373 * Find the class that defines the named member. Look up the hierarchy
3374 * starting at "cl".
3375 *
3376 * Return the class that defines the member "name", else NULL.
3377 * Fill in "p_m", if specified, for ocmember_T in found class.
3378 */
3379// NOTE: if useful for something could also indirectly return vartype and idx.
3380 static class_T *
3381class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m)
3382{
3383 class_T *cl_found = NULL;
3384 vartype_T vartype = VAR_UNKNOWN;
3385 ocmember_T *m_found = NULL;
3386
3387 len = len != 0 ? len : STRLEN(name);
3388
3389 // Loop assumes if member is not defined in "cl", then it is not
3390 // defined in any super class; the last class where it's found is the
3391 // class where it is defined. Once the vartype is found, the other
3392 // type is no longer checked.
3393 for (class_T *super = cl; super != NULL; super = super->class_extends)
3394 {
3395 class_T *cl_tmp = NULL;
3396 ocmember_T *m = NULL;
3397 if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT)
3398 {
3399 if ((m = object_member_lookup(super, name, len, NULL)) != NULL)
3400 {
3401 cl_tmp = super;
3402 vartype = VAR_OBJECT;
3403 }
3404 }
3405 if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS)
3406 {
3407 if (( m = class_member_lookup(super, name, len, NULL)) != NULL)
3408 {
3409 cl_tmp = super;
Zdenek Dohnal215c82d2024-12-04 20:19:40 +01003410 vartype = VAR_CLASS;
Ernie Raele6c9aa52023-10-06 19:55:52 +02003411 }
3412 }
3413 if (cl_tmp == NULL)
3414 break; // member is not in this or any super class.
3415 cl_found = cl_tmp;
3416 m_found = m;
3417 }
3418 if (p_m != NULL)
3419 *p_m = m_found;
3420 return cl_found;
3421}
3422
3423/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003424 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
3425 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
3426 *
3427 * Returns a pointer to the method structure if variable is found.
3428 * Otherwise returns NULL. The method variable index is set in "*idx".
3429 */
3430 ufunc_T *
3431method_lookup(
3432 class_T *cl,
3433 vartype_T v_type,
3434 char_u *name,
3435 size_t namelen,
3436 int *idx)
3437{
3438 if (v_type == VAR_CLASS)
3439 return class_method_lookup(cl, name, namelen, idx);
3440 else
3441 return object_method_lookup(cl, name, namelen, idx);
3442}
3443
3444/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00003445 * Return TRUE if current context "cctx_arg" is inside class "cl".
3446 * Return FALSE if not.
3447 */
3448 int
3449inside_class(cctx_T *cctx_arg, class_T *cl)
3450{
3451 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02003452 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003453 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00003454 return TRUE;
3455 return FALSE;
3456}
3457
3458/*
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01003459 * Return TRUE if object/class variable "m" is read-only.
3460 * Also give an error message.
3461 */
3462 int
3463oc_var_check_ro(class_T *cl, ocmember_T *m)
3464{
3465 if (m->ocm_flags & (OCMFLAG_FINAL | OCMFLAG_CONST))
3466 {
3467 semsg(_(e_cannot_change_readonly_variable_str_in_class_str),
3468 m->ocm_name, cl->class_name);
3469 return TRUE;
3470 }
3471 return FALSE;
3472}
3473
3474/*
3475 * Lock all the constant object variables. Called after creating and
3476 * initializing a new object.
3477 */
3478 void
3479obj_lock_const_vars(object_T *obj)
3480{
3481 for (int i = 0; i < obj->obj_class->class_obj_member_count; i++)
3482 {
3483 ocmember_T *ocm = &obj->obj_class->class_obj_members[i];
3484 if (ocm->ocm_flags & OCMFLAG_CONST)
3485 {
3486 typval_T *mtv = ((typval_T *)(obj + 1)) + i;
3487 item_lock(mtv, DICT_MAXNEST, TRUE, TRUE);
3488 }
3489 }
3490}
3491
3492/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003493 * Make a copy of an object.
3494 */
3495 void
3496copy_object(typval_T *from, typval_T *to)
3497{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003498 if (from->vval.v_object == NULL)
3499 to->vval.v_object = NULL;
3500 else
3501 {
3502 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003503 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003504 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003505}
3506
3507/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003508 * Make a copy of a class.
3509 */
3510 void
3511copy_class(typval_T *from, typval_T *to)
3512{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003513 if (from->vval.v_class == NULL)
3514 to->vval.v_class = NULL;
3515 else
3516 {
3517 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003518 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003519 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003520}
3521
3522/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003523 * Free the class "cl" and its contents.
3524 */
3525 static void
3526class_free(class_T *cl)
3527{
3528 // Freeing what the class contains may recursively come back here.
3529 // Clear "class_name" first, if it is NULL the class does not need to
3530 // be freed.
3531 VIM_CLEAR(cl->class_name);
3532
3533 class_unref(cl->class_extends);
3534
3535 for (int i = 0; i < cl->class_interface_count; ++i)
3536 {
3537 vim_free(((char_u **)cl->class_interfaces)[i]);
3538 if (cl->class_interfaces_cl[i] != NULL)
3539 class_unref(cl->class_interfaces_cl[i]);
3540 }
3541 vim_free(cl->class_interfaces);
3542 vim_free(cl->class_interfaces_cl);
3543
3544 itf2class_T *next;
3545 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
3546 {
3547 next = i2c->i2c_next;
3548 vim_free(i2c);
3549 }
3550
3551 for (int i = 0; i < cl->class_class_member_count; ++i)
3552 {
3553 ocmember_T *m = &cl->class_class_members[i];
3554 vim_free(m->ocm_name);
3555 vim_free(m->ocm_init);
3556 if (cl->class_members_tv != NULL)
3557 clear_tv(&cl->class_members_tv[i]);
3558 }
3559 vim_free(cl->class_class_members);
3560 vim_free(cl->class_members_tv);
3561
3562 for (int i = 0; i < cl->class_obj_member_count; ++i)
3563 {
3564 ocmember_T *m = &cl->class_obj_members[i];
3565 vim_free(m->ocm_name);
3566 vim_free(m->ocm_init);
3567 }
3568 vim_free(cl->class_obj_members);
3569
3570 for (int i = 0; i < cl->class_class_function_count; ++i)
3571 {
3572 ufunc_T *uf = cl->class_class_functions[i];
3573 func_clear_free(uf, FALSE);
3574 }
3575 vim_free(cl->class_class_functions);
3576
3577 for (int i = 0; i < cl->class_obj_method_count; ++i)
3578 {
3579 ufunc_T *uf = cl->class_obj_methods[i];
3580 func_clear_free(uf, FALSE);
3581 }
3582 vim_free(cl->class_obj_methods);
3583
3584 clear_type_list(&cl->class_type_list);
3585
3586 class_cleared(cl);
3587
3588 vim_free(cl);
3589}
3590
3591/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003592 * Unreference a class. Free it when the reference count goes down to zero.
3593 */
3594 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003595class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003596{
Bram Moolenaard505d172022-12-18 21:42:55 +00003597 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003598 class_free(cl);
3599}
3600
3601/*
3602 * Go through the list of all classes and free items without "copyID".
3603 */
3604 int
3605class_free_nonref(int copyID)
3606{
3607 int did_free = FALSE;
3608
3609 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003610 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003611 next_nonref_class = cl->class_next_used;
3612 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003613 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003614 // Free the class and items it contains.
3615 class_free(cl);
3616 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003617 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003618 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003619
3620 next_nonref_class = NULL;
3621 return did_free;
3622}
3623
3624 int
3625set_ref_in_classes(int copyID)
3626{
3627 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3628 set_ref_in_item_class(cl, copyID, NULL, NULL);
3629
3630 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003631}
3632
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003633static object_T *first_object = NULL;
3634
3635/*
3636 * Call this function when an object has been created. It will be added to the
3637 * list headed by "first_object".
3638 */
3639 void
3640object_created(object_T *obj)
3641{
3642 if (first_object != NULL)
3643 {
3644 obj->obj_next_used = first_object;
3645 first_object->obj_prev_used = obj;
3646 }
3647 first_object = obj;
3648}
3649
3650/*
3651 * Call this function when an object has been cleared and is about to be freed.
3652 * It is removed from the list headed by "first_object".
3653 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003654 static void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003655object_cleared(object_T *obj)
3656{
3657 if (obj->obj_next_used != NULL)
3658 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
3659 if (obj->obj_prev_used != NULL)
3660 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
3661 else if (first_object == obj)
3662 first_object = obj->obj_next_used;
3663}
3664
3665/*
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003666 * Free the contents of an object ignoring the reference count.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003667 */
3668 static void
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003669object_free_contents(object_T *obj)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003670{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003671 class_T *cl = obj->obj_class;
3672
3673 if (!cl)
3674 return;
3675
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003676 // Avoid a recursive call, it can happen if "obj" has a circular reference.
3677 obj->obj_refcount = INT_MAX;
3678
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003679 // the member values are just after the object structure
3680 typval_T *tv = (typval_T *)(obj + 1);
3681 for (int i = 0; i < cl->class_obj_member_count; ++i)
3682 clear_tv(tv + i);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003683}
3684
3685 static void
3686object_free_object(object_T *obj)
3687{
3688 class_T *cl = obj->obj_class;
3689
3690 if (!cl)
3691 return;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003692
3693 // Remove from the list headed by "first_object".
3694 object_cleared(obj);
3695
3696 vim_free(obj);
3697 class_unref(cl);
3698}
3699
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003700 static void
3701object_free(object_T *obj)
3702{
3703 if (in_free_unref_items)
3704 return;
3705
3706 object_free_contents(obj);
3707 object_free_object(obj);
3708}
3709
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003710/*
3711 * Unreference an object.
3712 */
3713 void
3714object_unref(object_T *obj)
3715{
3716 if (obj != NULL && --obj->obj_refcount <= 0)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003717 object_free(obj);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003718}
3719
3720/*
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003721 * Go through the list of all objects and free items without "copyID".
3722 */
3723 int
3724object_free_nonref(int copyID)
3725{
3726 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003727
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003728 for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003729 {
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003730 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3731 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003732 // Free the object contents. Object itself will be freed later.
3733 object_free_contents(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003734 did_free = TRUE;
3735 }
3736 }
3737
3738 return did_free;
3739}
3740
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003741 void
3742object_free_items(int copyID)
3743{
3744 object_T *obj_next;
3745
3746 for (object_T *obj = first_object; obj != NULL; obj = obj_next)
3747 {
3748 obj_next = obj->obj_next_used;
3749 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3750 object_free_object(obj);
3751 }
3752}
3753
LemonBoyafe04662023-08-23 21:08:11 +02003754/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003755 * Output message which takes a variable name and the class that defines it.
3756 * "cl" is that class where the name was found. Search "cl"'s hierarchy to
3757 * find the defining class.
3758 */
3759 void
3760emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl)
3761{
3762 ocmember_T *m;
3763 class_T *cl_def = class_defining_member(cl, name, len, &m);
3764 if (cl_def != NULL)
3765 semsg(_(msg), m->ocm_name, cl_def->class_name);
3766 else
3767 emsg(_(e_internal_error_please_report_a_bug));
3768}
3769
3770/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003771 * Echo a class or object method not found message.
3772 */
3773 void
3774method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3775{
3776 char_u *method_name = vim_strnsave(name, len);
3777 if ((v_type == VAR_OBJECT)
3778 && (class_method_idx(cl, name, len) >= 0))
3779 {
3780 // If this is a class method, then give a different error
3781 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003782 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003783 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003784 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003785 method_name, cl->class_name);
3786 }
3787 else if ((v_type == VAR_CLASS)
3788 && (object_method_idx(cl, name, len) >= 0))
3789 {
3790 // If this is an object method, then give a different error
3791 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003792 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003793 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003794 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003795 method_name, cl->class_name);
3796 }
3797 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003798 semsg(_(e_method_not_found_on_class_str_str), method_name,
zeertzjqd9be94c2024-07-14 10:20:20 +02003799 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003800 vim_free(method_name);
3801}
3802
3803/*
3804 * Echo a class or object member not found message.
3805 */
3806 void
3807member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3808{
3809 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
3810
3811 if (v_type == VAR_OBJECT)
3812 {
3813 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003814 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003815 varname, cl->class_name);
3816 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003817 semsg(_(e_variable_not_found_on_object_str_str), varname,
3818 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003819 }
3820 else
3821 {
3822 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003823 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003824 varname, cl->class_name);
3825 else
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01003826 {
3827 if (IS_ENUM(cl))
3828 semsg(_(e_enum_value_str_not_found_in_enum_str),
3829 varname, cl->class_name);
3830 else
3831 semsg(_(e_class_variable_str_not_found_in_class_str),
3832 varname, cl->class_name);
3833 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003834 }
3835 vim_free(varname);
3836}
3837
3838/*
Yegappan Lakshmanan4f32c832024-01-12 17:36:40 +01003839 * Compile all the class and object methods in "cl".
3840 */
3841 void
3842defcompile_class(class_T *cl)
3843{
3844 for (int loop = 1; loop <= 2; ++loop)
3845 {
3846 int func_count = loop == 1 ? cl->class_class_function_count
3847 : cl->class_obj_method_count;
3848 for (int i = 0; i < func_count; i++)
3849 {
3850 ufunc_T *ufunc = loop == 1 ? cl->class_class_functions[i]
3851 : cl->class_obj_methods[i];
Yegappan Lakshmanan1af0fbf2024-04-09 21:39:27 +02003852 // Don't compile abstract methods
3853 if (!IS_ABSTRACT_METHOD(ufunc))
3854 defcompile_function(ufunc, cl);
Yegappan Lakshmanan4f32c832024-01-12 17:36:40 +01003855 }
3856 }
3857}
3858
3859/*
3860 * Compile all the classes defined in the current script
3861 */
3862 void
3863defcompile_classes_in_script(void)
3864{
3865 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3866 {
3867 if (eval_variable(cl->class_name, 0, 0, NULL, NULL,
3868 EVAL_VAR_NOAUTOLOAD | EVAL_VAR_NO_FUNC) != FAIL)
3869 defcompile_class(cl);
3870 }
3871}
3872
3873/*
3874 * Returns TRUE if "name" is the name of a class. The typval for the class is
3875 * returned in "rettv".
3876 */
3877 int
3878is_class_name(char_u *name, typval_T *rettv)
3879{
3880 rettv->v_type = VAR_UNKNOWN;
3881
3882 if (eval_variable(name, 0, 0, rettv, NULL, EVAL_VAR_NOAUTOLOAD |
3883 EVAL_VAR_NO_FUNC) != FAIL)
3884 return rettv->v_type == VAR_CLASS;
3885 return FALSE;
3886}
3887
3888/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003889 * Calls the object builtin method "name" with arguments "argv". The value
3890 * returned by the builtin method is in "rettv". Returns OK or FAIL.
3891 */
3892 static int
3893object_call_builtin_method(
3894 object_T *obj,
3895 class_builtin_T builtin_method,
3896 int argc,
3897 typval_T *argv,
3898 typval_T *rettv)
3899{
3900 ufunc_T *uf;
3901 int midx;
3902
3903 if (obj == NULL)
3904 return FAIL;
3905
3906 uf = class_get_builtin_method(obj->obj_class, builtin_method, &midx);
3907 if (uf == NULL)
3908 return FAIL;
3909
3910 funccall_T *fc = create_funccal(uf, rettv);
3911 int r;
3912
3913 if (fc == NULL)
3914 return FAIL;
3915
3916 ++obj->obj_refcount;
3917
3918 r = call_def_function(uf, argc, argv, 0, NULL, obj, fc, rettv);
3919
3920 remove_funccal();
3921
3922 return r;
3923}
3924
3925/*
zeertzjqc029c132024-03-28 11:37:26 +01003926 * Calls the object "empty()" method and returns the method return value. In
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003927 * case of an error, returns TRUE.
3928 */
3929 int
3930object_empty(object_T *obj)
3931{
3932 typval_T rettv;
3933
3934 if (object_call_builtin_method(obj, CLASS_BUILTIN_EMPTY, 0, NULL, &rettv)
3935 == FAIL)
3936 return TRUE;
3937
3938 return tv_get_bool(&rettv);
3939}
3940
3941/*
3942 * Use the object "len()" method to get an object length. Returns 0 if the
3943 * method is not found or there is an error.
3944 */
3945 int
3946object_len(object_T *obj)
3947{
3948 typval_T rettv;
3949
3950 if (object_call_builtin_method(obj, CLASS_BUILTIN_LEN, 0, NULL, &rettv)
3951 == FAIL)
3952 return 0;
3953
3954 return tv_to_number(&rettv);
3955}
3956
3957/*
LemonBoy7b29cc92024-06-22 17:25:07 +02003958 * Return TRUE when two objects have exactly the same values.
3959 */
3960 int
3961object_equal(
3962 object_T *o1,
3963 object_T *o2,
Yinzuo Jiang7ccd1a22024-07-04 17:20:53 +02003964 int ic) // ignore case for strings
LemonBoy7b29cc92024-06-22 17:25:07 +02003965{
3966 class_T *cl1, *cl2;
3967
3968 if (o1 == o2)
3969 return TRUE;
Ernie Rael86257142024-06-23 09:54:45 +02003970 if (o1 == NULL || o2 == NULL)
3971 return FALSE;
LemonBoy7b29cc92024-06-22 17:25:07 +02003972
3973 cl1 = o1->obj_class;
3974 cl2 = o2->obj_class;
3975
3976 if (cl1 != cl2 || cl1 == NULL || cl2 == NULL)
3977 return FALSE;
3978
3979 for (int i = 0; i < cl1->class_obj_member_count; ++i)
Yinzuo Jiang7ccd1a22024-07-04 17:20:53 +02003980 if (!tv_equal((typval_T *)(o1 + 1) + i, (typval_T *)(o2 + 1) + i, ic))
LemonBoy7b29cc92024-06-22 17:25:07 +02003981 return FALSE;
3982
3983 return TRUE;
3984}
3985
3986/*
Ernie Rael05ff4e42024-07-04 16:50:11 +02003987 * Return a textual representation of object "obj".
3988 * "obj" must not be NULL.
3989 * May return NULL.
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003990 */
3991 char_u *
Yegappan Lakshmanan22029ed2024-05-20 13:57:11 +02003992object2string(
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003993 object_T *obj,
3994 char_u *numbuf,
3995 int copyID,
3996 int echo_style,
3997 int restore_copyID,
3998 int composite_val)
3999{
4000 typval_T rettv;
4001
4002 if (object_call_builtin_method(obj, CLASS_BUILTIN_STRING, 0, NULL, &rettv)
4003 == OK
4004 && rettv.vval.v_string != NULL)
4005 return rettv.vval.v_string;
Ernie Rael05ff4e42024-07-04 16:50:11 +02004006
4007 int ok = OK;
4008 class_T *cl = obj->obj_class;
4009 garray_T ga;
4010 ga_init2(&ga, 1, 50);
4011
4012 if (cl != NULL && IS_ENUM(cl))
4013 {
4014 ga_concat(&ga, (char_u *)"enum ");
4015 ga_concat(&ga, cl->class_name);
4016 char_u *enum_name = ((typval_T *)(obj + 1))->vval.v_string;
4017 ga_concat(&ga, (char_u *)".");
4018 ga_concat(&ga, enum_name);
4019 }
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004020 else
4021 {
Ernie Rael05ff4e42024-07-04 16:50:11 +02004022 ga_concat(&ga, (char_u *)"object of ");
4023 ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]"
4024 : cl->class_name);
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004025 }
Ernie Rael05ff4e42024-07-04 16:50:11 +02004026 if (cl != NULL)
4027 {
4028 ga_concat(&ga, (char_u *)" {");
4029 for (int i = 0; i < cl->class_obj_member_count; ++i)
4030 {
4031 if (i > 0)
4032 ga_concat(&ga, (char_u *)", ");
4033 ocmember_T *m = &cl->class_obj_members[i];
4034 ga_concat(&ga, m->ocm_name);
4035 ga_concat(&ga, (char_u *)": ");
4036 char_u *tf = NULL;
4037 char_u *s = echo_string_core((typval_T *)(obj + 1) + i,
4038 &tf, numbuf, copyID, echo_style,
4039 restore_copyID, composite_val);
4040 if (s != NULL)
4041 ga_concat(&ga, s);
4042 vim_free(tf);
4043 if (s == NULL || did_echo_string_emsg)
4044 {
4045 ok = FAIL;
4046 break;
4047 }
4048 line_breakcheck();
4049 }
4050 ga_concat(&ga, (char_u *)"}");
4051 }
4052 if (ok == FAIL)
4053 {
4054 vim_free(ga.ga_data);
4055 return NULL;
4056 }
Yegappan Lakshmanancb848b62025-01-20 21:38:09 +01004057 ga_append(&ga, NUL);
Ernie Rael05ff4e42024-07-04 16:50:11 +02004058 return (char_u *)ga.ga_data;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004059}
4060
4061/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02004062 * Return TRUE when the class "cl", its base class or one of the implemented
4063 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02004064 */
4065 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004066class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02004067{
4068 if (cl == other_cl)
4069 return TRUE;
4070
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004071 // Recursively check the base classes.
4072 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02004073 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004074 if (cl == other_cl)
4075 return TRUE;
4076 // Check the implemented interfaces and the super interfaces
4077 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02004078 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004079 class_T *intf = cl->class_interfaces_cl[i];
4080 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02004081 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004082 if (intf == other_cl)
4083 return TRUE;
4084 // check the super interfaces
4085 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02004086 }
4087 }
LemonBoyafe04662023-08-23 21:08:11 +02004088 }
4089
4090 return FALSE;
4091}
4092
4093/*
Ernie Rael2025af12023-12-12 16:58:00 +01004094 * "instanceof(object, classinfo, ...)" function
LemonBoyafe04662023-08-23 21:08:11 +02004095 */
4096 void
4097f_instanceof(typval_T *argvars, typval_T *rettv)
4098{
4099 typval_T *object_tv = &argvars[0];
4100 typval_T *classinfo_tv = &argvars[1];
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02004101 class_T *c;
LemonBoyafe04662023-08-23 21:08:11 +02004102
4103 rettv->vval.v_number = VVAL_FALSE;
4104
4105 if (check_for_object_arg(argvars, 0) == FAIL
Ernie Rael2025af12023-12-12 16:58:00 +01004106 || check_for_class_or_typealias_args(argvars, 1) == FAIL)
LemonBoyafe04662023-08-23 21:08:11 +02004107 return;
4108
Ernie Rael3da696d2023-09-19 20:14:18 +02004109 if (object_tv->vval.v_object == NULL)
4110 return;
4111
Ernie Rael2025af12023-12-12 16:58:00 +01004112 for (; classinfo_tv->v_type != VAR_UNKNOWN; ++classinfo_tv)
LemonBoyafe04662023-08-23 21:08:11 +02004113 {
Ernie Rael2025af12023-12-12 16:58:00 +01004114 if (classinfo_tv->v_type == VAR_TYPEALIAS)
4115 c = classinfo_tv->vval.v_typealias->ta_type->tt_class;
4116 else
4117 c = classinfo_tv->vval.v_class;
4118
4119 if (class_instance_of(object_tv->vval.v_object->obj_class, c))
LemonBoyafe04662023-08-23 21:08:11 +02004120 {
Ernie Rael2025af12023-12-12 16:58:00 +01004121 rettv->vval.v_number = VVAL_TRUE;
4122 return;
LemonBoyafe04662023-08-23 21:08:11 +02004123 }
4124 }
LemonBoyafe04662023-08-23 21:08:11 +02004125}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00004126
4127#endif // FEAT_EVAL