blob: a82cc13329b201ae658e5b504453b2b2b2dedf67 [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)
Yegappan Lakshmanan16f2d3a2025-02-24 19:23:43 +0100207 {
Bram Moolenaard505d172022-12-18 21:42:55 +0000208 m->ocm_init = init_expr;
Yegappan Lakshmanan16f2d3a2025-02-24 19:23:43 +0100209 // Save the script context, we need it when evaluating or compiling the
210 // initializer expression.
211 m->ocm_init_sctx = current_sctx;
212 m->ocm_init_sctx.sc_lnum += SOURCING_LNUM;
213 }
Bram Moolenaard505d172022-12-18 21:42:55 +0000214 ++gap->ga_len;
215 return OK;
216}
217
218/*
219 * Move the class or object members found while parsing a class into the class.
220 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000221 * "parent_members" points to the members in the parent class (if any)
222 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000223 * "members" will be set to the newly allocated array of members and
224 * "member_count" set to the number of members.
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100225 * Returns OK on success and FAIL on memory allocation failure.
Bram Moolenaard505d172022-12-18 21:42:55 +0000226 */
227 static int
228add_members_to_class(
229 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000230 ocmember_T *parent_members,
231 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000232 ocmember_T **members,
233 int *member_count)
234{
Bram Moolenaar83677162023-01-08 19:54:10 +0000235 *member_count = parent_count + gap->ga_len;
236 *members = *member_count == 0 ? NULL
237 : ALLOC_MULT(ocmember_T, *member_count);
238 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000239 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000240 for (int i = 0; i < parent_count; ++i)
241 {
242 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000243 ocmember_T *m = *members + i;
244 *m = parent_members[i];
245 m->ocm_name = vim_strsave(m->ocm_name);
246 if (m->ocm_init != NULL)
247 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000248 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000249 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000250 // new members are moved
251 mch_memmove(*members + parent_count,
252 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000253 VIM_CLEAR(gap->ga_data);
254 return OK;
255}
256
257/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000258 * Convert a member index "idx" of interface "itf" to the member index of class
259 * "cl" implementing that interface.
260 */
261 int
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200262object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000263{
Ernie Rael18143d32023-09-04 22:30:41 +0200264 if (idx >= (is_method ? itf->class_obj_method_count
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200265 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000266 {
267 siemsg("index %d out of range for interface %s", idx, itf->class_name);
268 return 0;
269 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200270
271 // If "cl" is the interface or the class that is extended, then the method
272 // index can be used directly and there is no need to search for the method
273 // index in one of the child classes.
274 if (cl == itf)
275 return idx;
276
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200277 itf2class_T *i2c = NULL;
278 int searching = TRUE;
279 int method_offset = 0;
280
Ernie Raelcf138d42023-09-06 20:45:03 +0200281 for (class_T *super = cl; super != NULL && searching;
282 super = super->class_extends)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200283 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200284 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200285 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200286 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
287 {
288 searching = FALSE;
289 break;
290 }
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200291 }
292 if (searching && is_method)
293 // The parent class methods are stored after the current class
294 // methods.
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200295 method_offset += super->class_obj_method_count_child;
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200296 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000297 if (i2c == NULL)
298 {
299 siemsg("class %s not found on interface %s",
300 cl->class_name, itf->class_name);
301 return 0;
302 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +0200303
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200304 // A table follows the i2c for the class
305 int *table = (int *)(i2c + 1);
306 // "method_offset" is 0, if method is in the current class. If method
307 // is in a parent class, then it is non-zero.
308 return table[idx] + method_offset;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000309}
310
311/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200312 * Check whether a class named "extends_name" is present. If the class is
313 * valid, then "extends_clp" is set with the class pointer.
314 * Returns TRUE if the class name "extends_names" is a valid class.
315 */
316 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200317validate_extends_class(
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100318 class_T *cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200319 char_u *extends_name,
320 class_T **extends_clp,
321 int is_class)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200322{
323 typval_T tv;
324 int success = FALSE;
325
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100326 if (STRCMP(cl->class_name, extends_name) == 0)
327 {
328 semsg(_(e_cannot_extend_str), extends_name);
329 return success;
330 }
331
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200332 tv.v_type = VAR_UNKNOWN;
333 if (eval_variable_import(extends_name, &tv) == FAIL)
334 {
335 semsg(_(e_class_name_not_found_str), extends_name);
336 return success;
337 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200338
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200339 if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100340 || (is_class && IS_INTERFACE(tv.vval.v_class))
341 || (!is_class && !IS_INTERFACE(tv.vval.v_class))
342 || (is_class && IS_ENUM(tv.vval.v_class)))
343 {
344 // a class cannot extend an interface
345 // an interface cannot extend a class
346 // a class cannot extend an enum.
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200347 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100348 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200349 else
350 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200351 class_T *extends_cl = tv.vval.v_class;
352 ++extends_cl->class_refcount;
353 *extends_clp = extends_cl;
354 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200355 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200356 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200357
358 return success;
359}
360
361/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200362 * Check method names in the parent class lineage to make sure the access is
363 * the same for overridden methods.
364 */
365 static int
366validate_extends_methods(
367 garray_T *objmethods_gap,
368 class_T *extends_cl)
369{
370 class_T *super = extends_cl;
371 int method_count = objmethods_gap->ga_len;
372 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
373
374 while (super != NULL)
375 {
376 int extends_method_count = super->class_obj_method_count_child;
377 if (extends_method_count == 0)
378 {
379 super = super->class_extends;
380 continue;
381 }
382
383 ufunc_T **extends_methods = super->class_obj_methods;
384
385 for (int i = 0; i < extends_method_count; i++)
386 {
387 char_u *pstr = extends_methods[i]->uf_name;
388 int extends_private = (*pstr == '_');
389 if (extends_private)
390 pstr++;
391
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200392 // When comparing the method names, ignore the access type (public
393 // and private methods are considered the same).
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200394 for (int j = 0; j < method_count; j++)
395 {
396 char_u *qstr = cl_fp[j]->uf_name;
397 int priv_method = (*qstr == '_');
398 if (priv_method)
399 qstr++;
400 if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
401 {
402 // Method access is different between the super class and
403 // the subclass
404 semsg(_(e_method_str_of_class_str_has_different_access),
405 cl_fp[j]->uf_name, super->class_name);
406 return FALSE;
407 }
408 }
409 }
410 super = super->class_extends;
411 }
412
413 return TRUE;
414}
415
416/*
417 * Check whether a object member variable in "objmembers_gap" is a duplicate of
418 * a member in any of the extended parent class lineage. Returns TRUE if there
419 * are no duplicates.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200420 */
421 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200422extends_check_dup_members(
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200423 garray_T *objmembers_gap,
424 class_T *extends_cl)
425{
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200426 int member_count = objmembers_gap->ga_len;
427 if (member_count == 0)
428 return TRUE;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200429
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200430 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
431
432 // Validate each member variable
433 for (int c_i = 0; c_i < member_count; c_i++)
434 {
435 class_T *p_cl = extends_cl;
436 ocmember_T *c_m = members + c_i;
437 char_u *pstr = (*c_m->ocm_name == '_')
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200438 ? c_m->ocm_name + 1 : c_m->ocm_name;
439
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200440 // Check in all the parent classes in the lineage
441 while (p_cl != NULL)
442 {
443 int p_member_count = p_cl->class_obj_member_count;
444 if (p_member_count == 0)
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200445 {
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200446 p_cl = p_cl->class_extends;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200447 continue;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200448 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200449 ocmember_T *p_members = p_cl->class_obj_members;
450
451 // Compare against all the members in the parent class
452 for (int p_i = 0; p_i < p_member_count; p_i++)
453 {
454 ocmember_T *p_m = p_members + p_i;
455 char_u *qstr = (*p_m->ocm_name == '_')
456 ? p_m->ocm_name + 1 : p_m->ocm_name;
457 if (STRCMP(pstr, qstr) == 0)
458 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200459 semsg(_(e_duplicate_variable_str), c_m->ocm_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200460 return FALSE;
461 }
462 }
463
464 p_cl = p_cl->class_extends;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200465 }
466 }
467
468 return TRUE;
469}
470
471/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200472 * Compare the variable type of interface variables in "objmembers_gap" against
473 * the variable in any of the extended super interface lineage. Used to
474 * compare the variable types when extending interfaces. Returns TRUE if the
475 * variable types are the same.
476 */
477 static int
478extends_check_intf_var_type(
479 garray_T *objmembers_gap,
480 class_T *extends_cl)
481{
482 int member_count = objmembers_gap->ga_len;
483 if (member_count == 0)
484 return TRUE;
485
486 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
487
488 // Validate each member variable
489 for (int c_i = 0; c_i < member_count; c_i++)
490 {
491 class_T *p_cl = extends_cl;
492 ocmember_T *c_m = members + c_i;
493 int var_found = FALSE;
494
495 // Check in all the parent classes in the lineage
496 while (p_cl != NULL && !var_found)
497 {
498 int p_member_count = p_cl->class_obj_member_count;
499 if (p_member_count == 0)
500 {
501 p_cl = p_cl->class_extends;
502 continue;
503 }
504 ocmember_T *p_members = p_cl->class_obj_members;
505
506 // Compare against all the members in the parent class
507 for (int p_i = 0; p_i < p_member_count; p_i++)
508 {
509 where_T where = WHERE_INIT;
510 ocmember_T *p_m = p_members + p_i;
511
512 if (STRCMP(p_m->ocm_name, c_m->ocm_name) != 0)
513 continue;
514
515 // Ensure the type is matching.
516 where.wt_func_name = (char *)c_m->ocm_name;
517 where.wt_kind = WT_MEMBER;
518
519 if (check_type(p_m->ocm_type, c_m->ocm_type, TRUE,
520 where) == FAIL)
521 return FALSE;
522
523 var_found = TRUE;
524 }
525
526 p_cl = p_cl->class_extends;
527 }
528 }
529
530 return TRUE;
531}
532
533/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200534 * When extending an abstract class, check whether all the abstract methods in
535 * the parent class are implemented. Returns TRUE if all the methods are
536 * implemented.
537 */
538 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200539validate_abstract_class_methods(
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200540 garray_T *classmethods_gap,
541 garray_T *objmethods_gap,
542 class_T *extends_cl)
543{
544 for (int loop = 1; loop <= 2; ++loop)
545 {
546 // loop == 1: check class methods
547 // loop == 2: check object methods
548 int extends_method_count = loop == 1
549 ? extends_cl->class_class_function_count
550 : extends_cl->class_obj_method_count;
551 if (extends_method_count == 0)
552 continue;
553
554 ufunc_T **extends_methods = loop == 1
555 ? extends_cl->class_class_functions
556 : extends_cl->class_obj_methods;
557
558 int method_count = loop == 1 ? classmethods_gap->ga_len
559 : objmethods_gap->ga_len;
560 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
561 ? classmethods_gap->ga_data
562 : objmethods_gap->ga_data);
563
564 for (int i = 0; i < extends_method_count; i++)
565 {
566 ufunc_T *uf = extends_methods[i];
Yegappan Lakshmanan1db15142023-09-19 20:34:05 +0200567 if (!IS_ABSTRACT_METHOD(uf))
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200568 continue;
569
Yegappan Lakshmanan68d08582025-02-09 19:39:52 +0100570 int concrete_method_found = FALSE;
571 int j = 0;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200572
Yegappan Lakshmanan68d08582025-02-09 19:39:52 +0100573 // Check if the abstract method is already implemented in one of
574 // the parent classes.
575 for (j = 0; !concrete_method_found && j < i; j++)
576 {
577 ufunc_T *uf2 = extends_methods[j];
578 if (!IS_ABSTRACT_METHOD(uf2) &&
579 STRCMP(uf->uf_name, uf2->uf_name) == 0)
580 concrete_method_found = TRUE;
581 }
582
583 if (concrete_method_found)
584 continue;
585
586 for (j = 0; j < method_count; j++)
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200587 {
588 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
589 {
Yegappan Lakshmanan68d08582025-02-09 19:39:52 +0100590 concrete_method_found = TRUE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200591 break;
592 }
593 }
594
Yegappan Lakshmanan68d08582025-02-09 19:39:52 +0100595 if (!concrete_method_found)
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200596 {
Yegappan Lakshmanan68d08582025-02-09 19:39:52 +0100597 semsg(_(e_abstract_method_str_not_implemented), uf->uf_name);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200598 return FALSE;
599 }
600 }
601 }
602
603 return TRUE;
604}
605
606/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200607 * Returns TRUE if the interface variable "if_var" is present in the list of
608 * variables in "cl_mt" or in the parent lineage of one of the extended classes
609 * in "extends_cl". For a class variable, 'is_class_var' is TRUE.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200610 */
611 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200612intf_variable_present(
613 char_u *intf_class_name,
614 ocmember_T *if_var,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200615 ocmember_T *cl_mt,
616 int cl_member_count,
617 class_T *extends_cl)
618{
619 int variable_present = FALSE;
620
621 for (int cl_i = 0; cl_i < cl_member_count; ++cl_i)
622 {
623 ocmember_T *m = &cl_mt[cl_i];
624 where_T where = WHERE_INIT;
625
626 if (STRCMP(if_var->ocm_name, m->ocm_name) != 0)
627 continue;
628
629 // Ensure the access type is same
630 if (if_var->ocm_access != m->ocm_access)
631 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200632 semsg(_(e_variable_str_of_interface_str_has_different_access),
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200633 if_var->ocm_name, intf_class_name);
634 return FALSE;
635 }
636
637 // Ensure the type is matching.
638 if (m->ocm_type == &t_any)
639 {
640 // variable type is not specified. Use the variable type in the
641 // interface.
642 m->ocm_type = if_var->ocm_type;
643 }
644 else
645 {
646 where.wt_func_name = (char *)m->ocm_name;
647 where.wt_kind = WT_MEMBER;
648 if (check_type(if_var->ocm_type, m->ocm_type, TRUE,
649 where) == FAIL)
650 return FALSE;
651 }
652
653 variable_present = TRUE;
654 break;
655 }
656
657 if (!variable_present && extends_cl != NULL)
658 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200659 int ext_cl_count = extends_cl->class_obj_member_count;
660 ocmember_T *ext_cl_mt = extends_cl->class_obj_members;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200661 return intf_variable_present(intf_class_name, if_var,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200662 ext_cl_mt, ext_cl_count,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200663 extends_cl->class_extends);
664 }
665
666 return variable_present;
667}
668
669/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200670 * Check the variables of the interface class "ifcl" match object variables
671 * ("objmembers_gap") of a class.
672 * Returns TRUE if the object variables names are valid.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200673 */
674 static int
675validate_interface_variables(
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200676 char_u *intf_class_name,
677 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200678 garray_T *objmembers_gap,
679 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200680{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200681 int if_count = ifcl->class_obj_member_count;
682 if (if_count == 0)
683 return TRUE;
684
685 ocmember_T *if_ms = ifcl->class_obj_members;
686 ocmember_T *cl_ms = (ocmember_T *)(objmembers_gap->ga_data);
687 int cl_count = objmembers_gap->ga_len;
688 for (int if_i = 0; if_i < if_count; ++if_i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200689 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200690 if (!intf_variable_present(intf_class_name, &if_ms[if_i], cl_ms,
691 cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200692 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200693 semsg(_(e_variable_str_of_interface_str_not_implemented),
694 if_ms[if_i].ocm_name, intf_class_name);
695 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200696 }
697 }
698
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200699 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200700}
701
702/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200703 * Returns TRUE if the method signature of "if_method" and "cl_method" matches.
704 */
705 static int
706intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method)
707{
708 where_T where = WHERE_INIT;
709
710 // Ensure the type is matching.
711 where.wt_func_name = (char *)if_method->uf_name;
712 where.wt_kind = WT_METHOD;
713 if (check_type(if_method->uf_func_type, cl_method->uf_func_type, TRUE,
714 where) == FAIL)
715 return FALSE;
716
717 return TRUE;
718}
719
720/*
721 * Returns TRUE if the interface method "if_ufunc" is present in the list of
722 * methods in "cl_fp" or in the parent lineage of one of the extended classes
723 * in "extends_cl". For a class method, 'is_class_method' is TRUE.
724 */
725 static int
726intf_method_present(
727 ufunc_T *if_ufunc,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200728 ufunc_T **cl_fp,
729 int cl_count,
730 class_T *extends_cl)
731{
732 int method_present = FALSE;
733
734 for (int cl_i = 0; cl_i < cl_count; ++cl_i)
735 {
736 char_u *cl_name = cl_fp[cl_i]->uf_name;
737 if (STRCMP(if_ufunc->uf_name, cl_name) == 0)
738 {
739 // Ensure the type is matching.
740 if (!intf_method_type_matches(if_ufunc, cl_fp[cl_i]))
741 return FALSE;
742 method_present = TRUE;
743 break;
744 }
745 }
746
747 if (!method_present && extends_cl != NULL)
748 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200749 ufunc_T **ext_cl_fp = (ufunc_T **)(extends_cl->class_obj_methods);
750 int ext_cl_count = extends_cl->class_obj_method_count;
751 return intf_method_present(if_ufunc, ext_cl_fp, ext_cl_count,
752 extends_cl->class_extends);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200753 }
754
755 return method_present;
756}
757
758/*
759 * Validate that a new class implements all the class/instance methods in the
760 * interface "ifcl". The new class methods are in "classfunctions_gap" and the
761 * new object methods are in "objmemthods_gap". Also validates the method
762 * types.
763 * Returns TRUE if all the interface class/object methods are implemented in
764 * the new class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200765 */
766 static int
767validate_interface_methods(
768 char_u *intf_class_name,
769 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200770 garray_T *objmethods_gap,
771 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200772{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200773 int if_count = ifcl->class_obj_method_count;
774 if (if_count == 0)
775 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200776
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200777 ufunc_T **if_fp = ifcl->class_obj_methods;
778 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
779 int cl_count = objmethods_gap->ga_len;
780 for (int if_i = 0; if_i < if_count; ++if_i)
781 {
782 char_u *if_name = if_fp[if_i]->uf_name;
783
784 if (!intf_method_present(if_fp[if_i], cl_fp, cl_count, extends_cl))
785 {
786 semsg(_(e_method_str_of_interface_str_not_implemented),
787 if_name, intf_class_name);
788 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200789 }
790 }
791
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200792 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200793}
794
795/*
796 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200797 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200798 * object methods and object members in the new class are in
799 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
800 * "objmembers_gap" respectively.
801 */
802 static int
803validate_implements_classes(
804 garray_T *impl_gap,
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +0100805 garray_T *intf_classes_gap,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200806 garray_T *objmethods_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200807 garray_T *objmembers_gap,
808 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200809{
810 int success = TRUE;
811
812 for (int i = 0; i < impl_gap->ga_len && success; ++i)
813 {
814 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
815 typval_T tv;
816 tv.v_type = VAR_UNKNOWN;
817 if (eval_variable_import(impl, &tv) == FAIL)
818 {
819 semsg(_(e_interface_name_not_found_str), impl);
820 success = FALSE;
821 break;
822 }
823
824 if (tv.v_type != VAR_CLASS
825 || tv.vval.v_class == NULL
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100826 || !IS_INTERFACE(tv.vval.v_class))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200827 {
828 semsg(_(e_not_valid_interface_str), impl);
829 success = FALSE;
830 clear_tv(&tv);
831 break;
832 }
833
834 class_T *ifcl = tv.vval.v_class;
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +0100835 if (ga_grow(intf_classes_gap, 1) == FAIL)
836 {
837 success = FALSE;
838 clear_tv(&tv);
839 break;
840 }
841 ((class_T **)intf_classes_gap->ga_data)[intf_classes_gap->ga_len]
842 = ifcl;
843 intf_classes_gap->ga_len++;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200844 ++ifcl->class_refcount;
845
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200846 // check the variables of the interface match the members of the class
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200847 success = validate_interface_variables(impl, ifcl, objmembers_gap,
848 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200849
850 // check the functions/methods of the interface match the
851 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200852 if (success)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200853 success = validate_interface_methods(impl, ifcl, objmethods_gap,
854 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200855 clear_tv(&tv);
856 }
857
858 return success;
859}
860
861/*
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +0100862 * Returns TRUE if the interface class "ifcl" is already present in the
863 * "intf_classes_gap" grow array.
864 */
865 static int
866is_interface_class_present(garray_T *intf_classes_gap, class_T *ifcl)
867{
868 for (int j = 0; j < intf_classes_gap->ga_len; j++)
869 {
870 if (((class_T **)intf_classes_gap)[j] == ifcl)
871 return TRUE;
872 }
873
874 return FALSE;
875}
876
877/*
878 * Add interface "ifcl" from a super class to "intf_classes_gap" and the class
879 * name to "impl_gap".
880 */
881 static int
882add_interface_from_super_class(
883 class_T *ifcl,
884 garray_T *impl_gap,
885 garray_T *intf_classes_gap)
886{
887 char_u *intf_name;
888
889 // Add the interface name to "impl_gap"
890 intf_name = vim_strsave(ifcl->class_name);
891 if (intf_name == NULL)
892 return FALSE;
893
894 if (ga_grow(impl_gap, 1) == FAIL)
895 return FALSE;
896
897 char_u **intf_names = (char_u **)impl_gap->ga_data;
898 intf_names[impl_gap->ga_len] = intf_name;
899 impl_gap->ga_len++;
900
901 // Add the interface class to "intf_classes_gap"
902 if (ga_grow(intf_classes_gap, 1) == FAIL)
903 return FALSE;
904
905 class_T **intf_classes = (class_T **)intf_classes_gap->ga_data;
906 intf_classes[intf_classes_gap->ga_len] = ifcl;
907 intf_classes_gap->ga_len++;
908 ++ifcl->class_refcount;
909
910 return TRUE;
911}
912
913/*
914 * Add "super" class interfaces to "intf_classes_gap" (if not present already)
915 * Add the interface class names to "impl_gap".
916 */
917 static int
918add_super_class_interfaces(
919 class_T *super,
920 garray_T *impl_gap,
921 garray_T *intf_classes_gap)
922{
923 // Iterate through all the interfaces implemented by "super"
924 for (int i = 0; i < super->class_interface_count; i++)
925 {
926 class_T *ifcl = super->class_interfaces_cl[i];
927
928 if (!is_interface_class_present(intf_classes_gap, ifcl))
929 add_interface_from_super_class(ifcl, impl_gap, intf_classes_gap);
930 }
931
932 return TRUE;
933}
934
935/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200936 * Check no function argument name is used as a class member.
937 * (Object members are always accessed with "this." prefix, so no need
938 * to check them.)
939 */
940 static int
941check_func_arg_names(
942 garray_T *classfunctions_gap,
943 garray_T *objmethods_gap,
944 garray_T *classmembers_gap)
945{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200946 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200947 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200948 {
949 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
950
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200951 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200952 {
953 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
954
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200955 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200956 {
957 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
958 garray_T *mgap = classmembers_gap;
959
960 // Check all the class member names
961 for (int mi = 0; mi < mgap->ga_len; ++mi)
962 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200963 char_u *mname =
964 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200965 if (STRCMP(aname, mname) == 0)
966 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200967 if (uf->uf_script_ctx.sc_sid > 0)
968 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
969
970 semsg(_(e_argument_already_declared_in_class_str),
971 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200972
973 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200974 }
975 }
976 }
977 }
978 }
979
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200980 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200981}
982
983/*
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200984 * Returns TRUE if 'varname' is a reserved keyword name
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200985 */
986 static int
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200987is_reserved_varname(char_u *varname, char_u *varname_end)
988{
989 int reserved = FALSE;
990 char_u save_varname_end = *varname_end;
991 *varname_end = NUL;
992
993 reserved = check_reserved_name(varname, FALSE) == FAIL;
994
995 *varname_end = save_varname_end;
996
997 return reserved;
998}
999
1000/*
1001 * Returns TRUE if the variable "varname" is already defined either as a class
1002 * variable or as an object variable.
1003 */
1004 static int
1005is_duplicate_variable(
1006 garray_T *class_members,
1007 garray_T *obj_members,
1008 char_u *varname,
1009 char_u *varname_end)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001010{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001011 char_u *name = vim_strnsave(varname, varname_end - varname);
1012 char_u *pstr = (*name == '_') ? name + 1 : name;
1013 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001014
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001015 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001016 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001017 // loop == 1: class variables, loop == 2: object variables
1018 garray_T *vgap = (loop == 1) ? class_members : obj_members;
1019 for (int i = 0; i < vgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001020 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001021 ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i;
1022 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1
1023 : m->ocm_name;
1024 if (STRCMP(pstr, qstr) == 0)
1025 {
1026 semsg(_(e_duplicate_variable_str), name);
1027 dup = TRUE;
1028 break;
1029 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001030 }
1031 }
1032
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001033 vim_free(name);
1034 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001035}
1036
1037/*
1038 * Returns TRUE if the method "name" is already defined.
1039 */
1040 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001041is_duplicate_method(
1042 garray_T *classmethods_gap,
1043 garray_T *objmethods_gap,
1044 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001045{
1046 char_u *pstr = (*name == '_') ? name + 1 : name;
1047
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001048 // loop 1: class methods, loop 2: object methods
1049 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001050 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001051 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
1052 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001053 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001054 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
1055 char_u *qstr = *n == '_' ? n + 1 : n;
1056 if (STRCMP(pstr, qstr) == 0)
1057 {
1058 semsg(_(e_duplicate_function_str), name);
1059 return TRUE;
1060 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001061 }
1062 }
1063
1064 return FALSE;
1065}
1066
1067/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001068 * Returns TRUE if the constructor is valid.
1069 */
1070 static int
1071is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
1072{
1073 // Constructors are not allowed in abstract classes.
1074 if (is_abstract)
1075 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001076 emsg(_(e_cannot_define_new_method_in_abstract_class));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001077 return FALSE;
1078 }
1079 // A constructor is always static, no need to define it so.
1080 if (has_static)
1081 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001082 emsg(_(e_cannot_define_new_method_as_static));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001083 return FALSE;
1084 }
1085 // A return type should not be specified for the new()
1086 // constructor method.
1087 if (uf->uf_ret_type->tt_type != VAR_VOID)
1088 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001089 emsg(_(e_cannot_use_a_return_type_with_new_method));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001090 return FALSE;
1091 }
1092 return TRUE;
1093}
1094
1095/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001096 * Returns TRUE if 'uf' is a supported builtin method and has the correct
1097 * method signature.
1098 */
1099 static int
1100object_check_builtin_method_sig(ufunc_T *uf)
1101{
1102 char_u *name = uf->uf_name;
1103 int valid = FALSE;
1104 type_T method_sig;
1105 type_T method_rt;
1106 where_T where = WHERE_INIT;
1107
1108 // validate the method signature
1109 CLEAR_FIELD(method_sig);
1110 CLEAR_FIELD(method_rt);
1111 method_sig.tt_type = VAR_FUNC;
1112
1113 if (STRCMP(name, "len") == 0)
1114 {
1115 // def __len(): number
1116 method_rt.tt_type = VAR_NUMBER;
1117 method_sig.tt_member = &method_rt;
1118 valid = TRUE;
1119 }
1120 else if (STRCMP(name, "empty") == 0)
1121 {
1122 // def __empty(): bool
1123 method_rt.tt_type = VAR_BOOL;
1124 method_sig.tt_member = &method_rt;
1125 valid = TRUE;
1126 }
1127 else if (STRCMP(name, "string") == 0)
1128 {
1129 // def __string(): string
1130 method_rt.tt_type = VAR_STRING;
1131 method_sig.tt_member = &method_rt;
1132 valid = TRUE;
1133 }
1134 else
1135 semsg(_(e_builtin_object_method_str_not_supported), uf->uf_name);
1136
1137 where.wt_func_name = (char *)uf->uf_name;
1138 where.wt_kind = WT_METHOD;
1139 if (valid && !check_type(&method_sig, uf->uf_func_type, TRUE, where))
1140 valid = FALSE;
1141
1142 return valid;
1143}
1144
1145/*
1146 * Returns TRUE if "funcname" is a supported builtin object method name
1147 */
1148 int
1149is_valid_builtin_obj_methodname(char_u *funcname)
1150{
1151 switch (funcname[0])
1152 {
1153 case 'e':
1154 return STRNCMP(funcname, "empty", 5) == 0;
1155
1156 case 'l':
1157 return STRNCMP(funcname, "len", 3) == 0;
1158
1159 case 'n':
1160 return STRNCMP(funcname, "new", 3) == 0;
1161
1162 case 's':
1163 return STRNCMP(funcname, "string", 6) == 0;
1164 }
1165
1166 return FALSE;
1167}
1168
1169
1170/*
1171 * Returns the builtin method "name" in object "obj". Returns NULL if the
1172 * method is not found.
1173 */
1174 ufunc_T *
1175class_get_builtin_method(
1176 class_T *cl,
1177 class_builtin_T builtin_method,
1178 int *method_idx)
1179{
1180 *method_idx = -1;
1181
1182 if (cl == NULL)
1183 return NULL;
1184
1185 *method_idx = cl->class_builtin_methods[builtin_method];
1186 return *method_idx != -1 ? cl->class_obj_methods[*method_idx] : NULL;
1187}
1188
1189/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001190 * Update the interface class lookup table for the member index on the
1191 * interface to the member index in the class implementing the interface.
1192 * And a lookup table for the object method index on the interface
1193 * to the object method index in the class implementing the interface.
1194 * This is also used for updating the lookup table for the extended class
1195 * hierarchy.
1196 */
1197 static int
1198update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001199 class_T *ifcl,
1200 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001201 garray_T *objmethods,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001202 int pobj_method_offset)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001203{
1204 if (ifcl == NULL)
1205 return OK;
1206
1207 // Table for members.
1208 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001209 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001210 if (if2cl == NULL)
1211 return FAIL;
1212 if2cl->i2c_next = ifcl->class_itf2class;
1213 ifcl->class_itf2class = if2cl;
1214 if2cl->i2c_class = cl;
1215 if2cl->i2c_is_method = FALSE;
1216
1217 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
1218 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
1219 {
1220 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001221 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001222 {
1223 int *table = (int *)(if2cl + 1);
1224 table[if_i] = cl_i;
1225 break;
1226 }
1227 }
1228
1229 // Table for methods.
1230 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001231 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001232 if (if2cl == NULL)
1233 return FAIL;
1234 if2cl->i2c_next = ifcl->class_itf2class;
1235 ifcl->class_itf2class = if2cl;
1236 if2cl->i2c_class = cl;
1237 if2cl->i2c_is_method = TRUE;
1238
1239 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
1240 {
1241 int done = FALSE;
1242 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
1243 {
1244 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001245 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001246 {
1247 int *table = (int *)(if2cl + 1);
1248 table[if_i] = cl_i;
1249 done = TRUE;
1250 break;
1251 }
1252 }
1253
1254 // extended class object method is not overridden by the child class.
1255 // Keep the method declared in one of the parent classes in the
1256 // lineage.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001257 if (!done)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001258 {
1259 // If "ifcl" is not the immediate parent of "cl", then search in
1260 // the intermediate parent classes.
1261 if (cl->class_extends != ifcl)
1262 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001263 class_T *parent = cl->class_extends;
1264 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001265
1266 while (!done && parent != NULL && parent != ifcl)
1267 {
1268
1269 for (int cl_i = 0;
1270 cl_i < parent->class_obj_method_count_child; ++cl_i)
1271 {
1272 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
1273 parent->class_obj_methods[cl_i]->uf_name)
1274 == 0)
1275 {
1276 int *table = (int *)(if2cl + 1);
1277 table[if_i] = method_offset + cl_i;
1278 done = TRUE;
1279 break;
1280 }
1281 }
1282 method_offset += parent->class_obj_method_count_child;
1283 parent = parent->class_extends;
1284 }
1285 }
1286
1287 if (!done)
1288 {
1289 int *table = (int *)(if2cl + 1);
1290 table[if_i] = pobj_method_offset + if_i;
1291 }
1292 }
1293 }
1294
1295 return OK;
1296}
1297
1298/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001299 * Update the member and object method lookup tables for a new class in the
1300 * interface class.
1301 * For each interface add a lookup table for the member index on the interface
1302 * to the member index in the new class. And a lookup table for the object
1303 * method index on the interface to the object method index in the new class.
1304 */
1305 static int
1306add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
1307{
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001308 // update the lookup table for all the implemented interfaces
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001309 for (int i = 0; i < cl->class_interface_count; ++i)
1310 {
1311 class_T *ifcl = cl->class_interfaces_cl[i];
1312
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001313 // update the lookup table for this interface and all its super
1314 // interfaces.
1315 while (ifcl != NULL)
1316 {
1317 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
1318 0) == FAIL)
1319 return FAIL;
1320 ifcl = ifcl->class_extends;
1321 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001322 }
1323
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001324 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001325 if (extends_cl != NULL)
1326 {
1327 class_T *pclass = extends_cl;
1328 int pobj_method_offset = objmethods_gap->ga_len;
1329
1330 // Update the entire lineage of extended classes.
1331 while (pclass != NULL)
1332 {
1333 if (update_member_method_lookup_table(pclass, cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001334 objmethods_gap, pobj_method_offset) == FAIL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001335 return FAIL;
1336
1337 pobj_method_offset += pclass->class_obj_method_count_child;
1338 pclass = pclass->class_extends;
1339 }
1340 }
1341
1342 return OK;
1343}
1344
1345/*
1346 * Add class members to a new class. Allocate a typval for each class member
1347 * and initialize it.
1348 */
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001349 static int
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001350add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001351{
1352 // Allocate a typval for each class member and initialize it.
1353 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
1354 cl->class_class_member_count);
1355 if (cl->class_members_tv == NULL)
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001356 return FAIL;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001357
1358 for (int i = 0; i < cl->class_class_member_count; ++i)
1359 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001360 ocmember_T *m = &cl->class_class_members[i];
1361 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001362 if (m->ocm_init != NULL)
1363 {
Yegappan Lakshmanan16f2d3a2025-02-24 19:23:43 +01001364 sctx_T save_current_sctx = current_sctx;
1365
1366 current_sctx = m->ocm_init_sctx;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001367 typval_T *etv = eval_expr(m->ocm_init, eap);
Yegappan Lakshmanan16f2d3a2025-02-24 19:23:43 +01001368 current_sctx = save_current_sctx;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001369 if (etv == NULL)
1370 return FAIL;
1371
1372 if (m->ocm_type->tt_type == VAR_ANY
1373 && !(m->ocm_flags & OCMFLAG_HAS_TYPE)
1374 && etv->v_type != VAR_SPECIAL)
1375 // If the member variable type is not yet set, then use
1376 // the initialization expression type.
1377 m->ocm_type = typval2type(etv, get_copyID(),
1378 type_list_gap,
1379 TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
1380 *tv = *etv;
1381 vim_free(etv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001382 }
1383 else
1384 {
1385 // TODO: proper default value
1386 tv->v_type = m->ocm_type->tt_type;
1387 tv->vval.v_string = NULL;
1388 }
LemonBoyf4af3312024-07-04 13:43:12 +02001389 set_tv_type(tv, m->ocm_type);
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001390 if (m->ocm_flags & OCMFLAG_CONST)
1391 item_lock(tv, DICT_MAXNEST, TRUE, TRUE);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001392 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001393
1394 return OK;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001395}
1396
1397/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001398 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001399 */
1400 static void
1401add_default_constructor(
1402 class_T *cl,
1403 garray_T *classfunctions_gap,
1404 garray_T *type_list_gap)
1405{
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001406 garray_T fga;
1407 int is_enum = IS_ENUM(cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001408
1409 ga_init2(&fga, 1, 1000);
1410 ga_concat(&fga, (char_u *)"new(");
1411 for (int i = 0; i < cl->class_obj_member_count; ++i)
1412 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001413 if (i < 2 && is_enum)
1414 // The first two object variables in an enum are the enum value
1415 // name and ordinal. Don't initialize these object variables in
1416 // the default constructor as they are already initialized right
1417 // after creating the object.
1418 continue;
1419
1420 if (i > (is_enum ? 2 : 0))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001421 ga_concat(&fga, (char_u *)", ");
1422 ga_concat(&fga, (char_u *)"this.");
1423 ocmember_T *m = cl->class_obj_members + i;
1424 ga_concat(&fga, (char_u *)m->ocm_name);
1425 ga_concat(&fga, (char_u *)" = v:none");
1426 }
1427 ga_concat(&fga, (char_u *)")\nenddef\n");
1428 ga_append(&fga, NUL);
1429
1430 exarg_T fea;
1431 CLEAR_FIELD(fea);
1432 fea.cmdidx = CMD_def;
1433 fea.cmd = fea.arg = fga.ga_data;
1434
1435 garray_T lines_to_free;
1436 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1437
h-eastb895b0f2023-09-24 15:46:31 +02001438 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
1439 cl->class_obj_members, cl->class_obj_member_count);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001440
1441 ga_clear_strings(&lines_to_free);
1442 vim_free(fga.ga_data);
1443
1444 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1445 {
1446 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001447 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001448 ++classfunctions_gap->ga_len;
1449
1450 nf->uf_flags |= FC_NEW;
1451 nf->uf_ret_type = get_type_ptr(type_list_gap);
1452 if (nf->uf_ret_type != NULL)
1453 {
1454 nf->uf_ret_type->tt_type = VAR_OBJECT;
1455 nf->uf_ret_type->tt_class = cl;
1456 nf->uf_ret_type->tt_argcount = 0;
1457 nf->uf_ret_type->tt_args = NULL;
1458 }
1459 }
1460}
1461
1462/*
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001463 * Add the class methods and object methods to the new class "cl".
1464 * When extending a class "extends_cl", add the instance methods from the
1465 * parent class also.
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001466 * Returns OK on success and FAIL on memory allocation failure.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001467 */
1468 static int
1469add_classfuncs_objmethods(
1470 class_T *cl,
1471 class_T *extends_cl,
1472 garray_T *classfunctions_gap,
1473 garray_T *objmethods_gap)
1474{
1475 // loop 1: class functions, loop 2: object methods
1476 for (int loop = 1; loop <= 2; ++loop)
1477 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001478 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1479 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001480 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001481 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001482 : &cl->class_obj_methods;
1483
1484 int parent_count = 0;
1485 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001486 // Include object methods from the parent.
1487 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001488 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001489 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001490 : extends_cl->class_obj_method_count;
1491
1492 *fcount = parent_count + gap->ga_len;
1493 if (*fcount == 0)
1494 {
1495 *fup = NULL;
1496 continue;
1497 }
1498 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1499 if (*fup == NULL)
1500 return FAIL;
1501
1502 if (gap->ga_len != 0)
1503 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001504 VIM_CLEAR(gap->ga_data);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001505 if (loop == 1)
1506 cl->class_class_function_count_child = gap->ga_len;
1507 else
1508 cl->class_obj_method_count_child = gap->ga_len;
1509
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001510 if (loop == 2)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001511 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001512 // Copy instance methods from the parent.
1513
1514 for (int i = 0; i < parent_count; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001515 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001516 // Can't use the same parent function, because "uf_class" is
1517 // different and compilation will have a different result.
1518 // Put them after the functions in the current class, object
1519 // methods may be overruled, then "super.Method()" is used to
1520 // find a method from the parent.
1521 ufunc_T *pf = (extends_cl->class_obj_methods)[i];
1522 (*fup)[gap->ga_len + i] = copy_function(pf);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001523
1524 // If the child class overrides a function from the parent
1525 // the signature must be equal.
1526 char_u *pname = pf->uf_name;
1527 for (int ci = 0; ci < gap->ga_len; ++ci)
1528 {
1529 ufunc_T *cf = (*fup)[ci];
1530 char_u *cname = cf->uf_name;
1531 if (STRCMP(pname, cname) == 0)
1532 {
1533 where_T where = WHERE_INIT;
1534 where.wt_func_name = (char *)pname;
1535 where.wt_kind = WT_METHOD;
1536 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1537 TRUE, where);
1538 }
1539 }
1540 }
1541 }
1542
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001543 // Set the class pointer on all the functions and object methods.
1544 for (int i = 0; i < *fcount; ++i)
1545 {
1546 ufunc_T *fp = (*fup)[i];
1547 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001548 if (i < gap->ga_len)
1549 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001550 if (loop == 2)
1551 fp->uf_flags |= FC_OBJECT;
1552 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001553
1554 ga_clear(gap);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001555 }
1556
1557 return OK;
1558}
1559
1560/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001561 * Update the index of object methods called by builtin functions.
1562 */
1563 static void
1564update_builtin_method_index(class_T *cl)
1565{
1566 int i;
1567
1568 for (i = 0; i < CLASS_BUILTIN_MAX; i++)
1569 cl->class_builtin_methods[i] = -1;
1570
1571 for (i = 0; i < cl->class_obj_method_count; i++)
1572 {
1573 ufunc_T *uf = cl->class_obj_methods[i];
1574
1575 if (cl->class_builtin_methods[CLASS_BUILTIN_STRING] == -1
1576 && STRCMP(uf->uf_name, "string") == 0)
1577 cl->class_builtin_methods[CLASS_BUILTIN_STRING] = i;
1578 else if (cl->class_builtin_methods[CLASS_BUILTIN_EMPTY] == -1 &&
1579 STRCMP(uf->uf_name, "empty") == 0)
1580 cl->class_builtin_methods[CLASS_BUILTIN_EMPTY] = i;
1581 else if (cl->class_builtin_methods[CLASS_BUILTIN_LEN] == -1 &&
1582 STRCMP(uf->uf_name, "len") == 0)
1583 cl->class_builtin_methods[CLASS_BUILTIN_LEN] = i;
1584 }
1585}
1586
1587/*
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001588 * Return the end of the class name starting at "arg". Valid characters in a
1589 * class name are alphanumeric characters and "_". Also handles imported class
1590 * names.
1591 */
1592 static char_u *
1593find_class_name_end(char_u *arg)
1594{
1595 char_u *end = arg;
1596
1597 while (ASCII_ISALNUM(*end) || *end == '_'
1598 || (*end == '.' && (ASCII_ISALNUM(end[1]) || end[1] == '_')))
1599 ++end;
1600
1601 return end;
1602}
1603
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001604/*
1605 * Returns TRUE if the enum value "varname" is already defined.
1606 */
1607 static int
1608is_duplicate_enum(
1609 garray_T *enum_gap,
1610 char_u *varname,
1611 char_u *varname_end)
1612{
1613 char_u *name = vim_strnsave(varname, varname_end - varname);
1614 int dup = FALSE;
1615
1616 for (int i = 0; i < enum_gap->ga_len; ++i)
1617 {
1618 ocmember_T *m = ((ocmember_T *)enum_gap->ga_data) + i;
1619 if (STRCMP(name, m->ocm_name) == 0)
1620 {
1621 semsg(_(e_duplicate_enum_str), name);
1622 dup = TRUE;
1623 break;
1624 }
1625 }
1626
1627 vim_free(name);
1628 return dup;
1629}
1630
1631/*
1632 * Parse the enum values in "line" separated by comma and add them to "gap".
1633 * If the last enum value is found, then "enum_end" is set to TRUE.
1634 */
1635 static int
1636enum_parse_values(
1637 exarg_T *eap,
1638 class_T *en,
1639 char_u *line,
1640 garray_T *gap,
1641 int *num_enum_values,
1642 int *enum_end)
1643{
1644 evalarg_T evalarg;
1645 char_u *p = line;
1646 char initexpr_buf[1024];
1647 char_u last_char = NUL;
1648 int rc = OK;
1649
1650 fill_evalarg_from_eap(&evalarg, eap, FALSE);
1651
1652 int did_emsg_before = did_emsg;
1653 while (*p != NUL)
1654 {
1655 // ignore comment
1656 if (*p == '#')
1657 break;
1658
1659 if (!eval_isnamec1(*p))
1660 {
1661 semsg(_(e_invalid_enum_value_declaration_str), p);
1662 break;
1663 }
1664
1665 char_u *eni_name_start = p;
1666 char_u *eni_name_end = to_name_end(p, FALSE);
1667
1668 if (is_duplicate_enum(gap, eni_name_start, eni_name_end))
1669 break;
1670
1671 p = skipwhite(eni_name_end);
1672
1673 char_u *init_expr = NULL;
1674 if (*p == '(')
1675 {
1676 if (VIM_ISWHITE(p[-1]))
1677 {
1678 semsg(_(e_no_white_space_allowed_before_str_str), "(", line);
1679 break;
1680 }
1681
1682 char_u *expr_start, *expr_end;
1683
1684 p = eni_name_start;
1685 (void)skip_expr_concatenate(&p, &expr_start, &expr_end, &evalarg);
1686
1687 while (*expr_start && *expr_start != '(')
1688 expr_start++;
1689
1690 if (expr_end > expr_start)
1691 init_expr = vim_strnsave(expr_start, expr_end - expr_start);
1692 }
1693
1694 if (init_expr == NULL)
1695 vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new()",
1696 en->class_name);
1697 else
1698 {
1699 vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new%s",
1700 en->class_name, init_expr);
1701 vim_free(init_expr);
1702 }
1703 if (add_member(gap, eni_name_start, eni_name_end, FALSE,
1704 TRUE, TRUE, TRUE, &en->class_object_type,
1705 vim_strsave((char_u *)initexpr_buf)) == FAIL)
1706 break;
1707
1708 ++*num_enum_values;
1709
1710 if (*p != '#')
1711 last_char = *p;
1712
1713 if (*p != NUL && *p != ',')
1714 break;
1715
1716 if (*p == ',')
1717 {
1718 if (!IS_WHITE_OR_NUL(p[1]))
1719 {
1720 semsg(_(e_white_space_required_after_str_str), ",", line);
1721 break;
1722 }
1723 if (VIM_ISWHITE(p[-1]))
1724 {
1725 semsg(_(e_no_white_space_allowed_before_str_str), ",", line);
1726 break;
1727 }
1728 p = skipwhite(p + 1);
1729 }
1730 }
1731
Doug Kearnsdbe39ed2025-01-04 17:12:24 +01001732 p = skipwhite(p);
1733
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001734 if (*p != NUL && *p != '#')
1735 {
1736 if (did_emsg == did_emsg_before)
1737 semsg(_(e_missing_comma_before_argument_str), p);
1738 rc = FAIL;
1739 }
1740
1741 if (last_char != ',')
1742 // last enum value should not be terminated by ","
1743 *enum_end = TRUE;
1744
1745 // Free the memory pointed by expr_start.
1746 clear_evalarg(&evalarg, NULL);
1747
1748 return rc;
1749}
1750
1751/*
1752 * Add the "values" class variable (List of enum value objects) to the enum
1753 * class "en"
1754 */
1755 static int
1756enum_add_values_member(
1757 class_T *en,
1758 garray_T *gap,
1759 int num_enum_values,
1760 garray_T *type_list_gap)
1761{
1762 garray_T fga;
1763 int rc = FAIL;
1764
1765 ga_init2(&fga, 1, 1000);
1766 ga_concat(&fga, (char_u *)"[");
1767 for (int i = 0; i < num_enum_values; ++i)
1768 {
1769 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
1770
1771 if (i > 0)
1772 ga_concat(&fga, (char_u *)", ");
1773 ga_concat(&fga, en->class_name);
1774 ga_concat(&fga, (char_u *)".");
1775 ga_concat(&fga, (char_u *)m->ocm_name);
1776 }
1777 ga_concat(&fga, (char_u *)"]");
1778 ga_append(&fga, NUL);
1779
1780 char_u *varname = (char_u *)"values";
1781
1782 type_T *type = get_type_ptr(type_list_gap);
1783 if (type == NULL)
1784 goto done;
1785
1786 type->tt_type = VAR_LIST;
1787 type->tt_member = get_type_ptr(type_list_gap);
1788 if (type->tt_member != NULL)
1789 {
1790 type->tt_member->tt_type = VAR_OBJECT;
1791 type->tt_member->tt_class = en;
1792 }
1793
1794 rc = add_member(gap, varname, varname + 6, FALSE, FALSE, TRUE, TRUE, type,
1795 vim_strsave((char_u *)fga.ga_data));
1796
1797done:
1798 vim_free(fga.ga_data);
1799
1800 return rc;
1801}
1802
1803/*
1804 * Clear the constructor method names in a enum class, so that an enum class
1805 * cannot be instantiated.
1806 */
1807 static void
1808enum_clear_constructors(class_T *en)
1809{
1810 for (int i = 0; i < en->class_class_function_count; ++i)
1811 {
1812 ufunc_T *fp = en->class_class_functions[i];
1813
1814 if (fp->uf_flags & FC_NEW)
1815 *fp->uf_name = NUL;
1816 }
1817}
1818
1819/*
1820 * Initialize the name and ordinal object variable in the enum value "enval" in
1821 * the enum "en". These values are set during the enum value object creation.
1822 */
1823 void
1824enum_set_internal_obj_vars(class_T *en, object_T *enval)
1825{
1826 int i;
1827
1828 for (i = 0; i < en->class_class_member_count; ++i)
1829 {
1830 typval_T *en_tv = en->class_members_tv + i;
1831 if (en_tv != NULL && en_tv->v_type == VAR_UNKNOWN)
1832 break;
1833 }
1834
1835 // First object variable is the name
1836 ocmember_T *value_ocm = en->class_class_members + i;
1837 typval_T *name_tv = (typval_T *)(enval + 1);
1838 name_tv->v_type = VAR_STRING;
1839 name_tv->vval.v_string = vim_strsave(value_ocm->ocm_name);
1840
1841 // Second object variable is the ordinal
1842 typval_T *ord_tv = (typval_T *)(name_tv + 1);
1843 ord_tv->v_type = VAR_NUMBER;
1844 ord_tv->vval.v_number = i;
1845}
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001846
1847/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001848 * Handle ":class" and ":abstract class" up to ":endclass".
h-eastaa979c72025-01-03 10:19:45 +01001849 * Handle ":enum" up to ":endenum".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001850 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001851 */
1852 void
1853ex_class(exarg_T *eap)
1854{
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001855 int is_class = eap->cmdidx == CMD_class;
1856 int is_abstract = eap->cmdidx == CMD_abstract;
1857 int is_enum = eap->cmdidx == CMD_enum;
1858 int is_interface;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001859 long start_lnum = SOURCING_LNUM;
1860 char_u *arg = eap->arg;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001861
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001862 if (is_abstract)
1863 {
1864 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1865 {
1866 semsg(_(e_invalid_argument_str), arg);
1867 return;
1868 }
1869 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001870 is_class = TRUE;
1871 }
1872
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001873 is_interface = !is_class && !is_enum;
1874
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001875 if (!current_script_is_vim9()
1876 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001877 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001878 {
1879 if (is_class)
1880 emsg(_(e_class_can_only_be_defined_in_vim9_script));
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001881 else if (is_enum)
1882 emsg(_(e_enum_can_only_be_defined_in_vim9_script));
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001883 else
1884 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1885 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001886 }
1887
1888 if (!ASCII_ISUPPER(*arg))
1889 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001890 if (is_class)
1891 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001892 else if (is_enum)
1893 semsg(_(e_enum_name_must_start_with_uppercase_letter_str), arg);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001894 else
1895 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1896 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001897 return;
1898 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001899 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1900 if (!IS_WHITE_OR_NUL(*name_end))
1901 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001902 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001903 return;
1904 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001905 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001906
1907 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001908 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001909
Bram Moolenaar83677162023-01-08 19:54:10 +00001910 // Name for "extends BaseClass"
1911 char_u *extends = NULL;
1912
Bram Moolenaar94674f22023-01-06 18:42:20 +00001913 // Names for "implements SomeInterface"
1914 garray_T ga_impl;
1915 ga_init2(&ga_impl, sizeof(char_u *), 5);
1916
1917 arg = skipwhite(name_end);
1918 while (*arg != NUL && *arg != '#' && *arg != '\n')
1919 {
1920 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001921 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001922 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1923 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001924 if (is_enum)
1925 {
1926 emsg(_(e_enum_cannot_extend_class));
1927 goto early_ret;
1928 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001929 if (extends != NULL)
1930 {
1931 emsg(_(e_duplicate_extends));
1932 goto early_ret;
1933 }
1934 arg = skipwhite(arg + 7);
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001935
1936 char_u *end = find_class_name_end(arg);
Bram Moolenaar83677162023-01-08 19:54:10 +00001937 if (!IS_WHITE_OR_NUL(*end))
1938 {
1939 semsg(_(e_white_space_required_after_name_str), arg);
1940 goto early_ret;
1941 }
1942 extends = vim_strnsave(arg, end - arg);
1943 if (extends == NULL)
1944 goto early_ret;
1945
1946 arg = skipwhite(end + 1);
1947 }
1948 else if (STRNCMP(arg, "implements", 10) == 0
1949 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001950 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001951 if (is_interface)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001952 {
1953 emsg(_(e_interface_cannot_use_implements));
1954 goto early_ret;
1955 }
1956
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001957 if (ga_impl.ga_len > 0)
1958 {
1959 emsg(_(e_duplicate_implements));
1960 goto early_ret;
1961 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001962 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001963
1964 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001965 {
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001966 char_u *impl_end = find_class_name_end(arg);
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001967 if ((!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1968 || (*impl_end == ','
1969 && !IS_WHITE_OR_NUL(*(impl_end + 1))))
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001970 {
1971 semsg(_(e_white_space_required_after_name_str), arg);
1972 goto early_ret;
1973 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001974 if (impl_end - arg == 0)
1975 {
1976 emsg(_(e_missing_name_after_implements));
1977 goto early_ret;
1978 }
1979
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001980 char_u *iname = vim_strnsave(arg, impl_end - arg);
1981 if (iname == NULL)
1982 goto early_ret;
1983 for (int i = 0; i < ga_impl.ga_len; ++i)
1984 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1985 {
1986 semsg(_(e_duplicate_interface_after_implements_str),
1987 iname);
1988 vim_free(iname);
1989 goto early_ret;
1990 }
1991 if (ga_add_string(&ga_impl, iname) == FAIL)
1992 {
1993 vim_free(iname);
1994 goto early_ret;
1995 }
1996 if (*impl_end != ',')
1997 {
1998 arg = skipwhite(impl_end);
1999 break;
2000 }
2001 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00002002 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00002003 }
2004 else
2005 {
2006 semsg(_(e_trailing_characters_str), arg);
2007early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00002008 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00002009 ga_clear_strings(&ga_impl);
2010 return;
2011 }
2012 }
2013
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002014 garray_T type_list; // list of pointers to allocated types
2015 ga_init2(&type_list, sizeof(type_T *), 10);
2016
Bram Moolenaard505d172022-12-18 21:42:55 +00002017 // Growarray with class members declared in the class.
2018 garray_T classmembers;
2019 ga_init2(&classmembers, sizeof(ocmember_T), 10);
2020
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002021 // Growarray with functions declared in the class.
2022 garray_T classfunctions;
2023 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00002024
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002025 // Growarray with object members declared in the class.
2026 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00002027 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002028
2029 // Growarray with object methods declared in the class.
2030 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002031 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002032
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002033 class_T *cl = NULL;
2034 class_T *extends_cl = NULL; // class from "extends" argument
2035 class_T **intf_classes = NULL;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002036 int num_enum_values = 0;
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002037
2038 cl = ALLOC_CLEAR_ONE(class_T);
2039 if (cl == NULL)
2040 goto cleanup;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002041
2042 if (is_enum)
2043 cl->class_flags = CLASS_ENUM;
2044 else if (is_interface)
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002045 cl->class_flags = CLASS_INTERFACE;
2046 else if (is_abstract)
2047 cl->class_flags = CLASS_ABSTRACT;
2048
2049 cl->class_refcount = 1;
2050 cl->class_name = vim_strnsave(name_start, name_end - name_start);
2051 if (cl->class_name == NULL)
2052 goto cleanup;
2053
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002054 cl->class_type.tt_type = VAR_CLASS;
2055 cl->class_type.tt_class = cl;
2056 cl->class_object_type.tt_type = VAR_OBJECT;
2057 cl->class_object_type.tt_class = cl;
2058
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002059 // Add the class to the script-local variables.
2060 // TODO: handle other context, e.g. in a function
2061 // TODO: does uf_hash need to be cleared?
2062 typval_T tv;
2063 tv.v_type = VAR_CLASS;
2064 tv.vval.v_class = cl;
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002065 SOURCING_LNUM = start_lnum;
Yegappan Lakshmanane9ae35f2025-02-27 19:12:00 +01002066 int rc = set_var_const(cl->class_name, 0, NULL, &tv, FALSE, 0, 0);
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002067 if (rc == FAIL)
2068 goto cleanup;
2069
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002070 if (is_enum)
2071 {
2072 // All the enum classes have the name and ordinal object variables.
2073 char_u *varname = (char_u *)"name";
2074 if (add_member(&objmembers, varname, varname + 4, FALSE, FALSE, TRUE,
2075 TRUE, &t_string, NULL) == FAIL)
2076 goto cleanup;
2077
2078 varname = (char_u *)"ordinal";
2079 if (add_member(&objmembers, varname, varname + 7, FALSE, FALSE, TRUE,
2080 TRUE, &t_number, NULL) == FAIL)
2081 goto cleanup;
2082 }
2083
2084 // "export class" gets used when creating the class, don't use "is_export"
2085 // for the items inside the class.
2086 is_export = FALSE;
2087
2088 // When parsing an enum definition, this denotes whether all the enumerated
2089 // values are parsed or not.
2090 int enum_end = FALSE;
2091
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002092 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00002093 * Go over the body of the class/interface until "endclass" or
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002094 * "endinterface" or "endenum" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002095 */
2096 char_u *theline = NULL;
2097 int success = FALSE;
2098 for (;;)
2099 {
2100 vim_free(theline);
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002101 theline = eap->ea_getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002102 if (theline == NULL)
2103 break;
2104 char_u *line = skipwhite(theline);
2105
Bram Moolenaar418b5472022-12-20 13:38:22 +00002106 // Skip empty and comment lines.
2107 if (*line == NUL)
2108 continue;
2109 if (*line == '#')
2110 {
2111 if (vim9_bad_comment(line))
2112 break;
2113 continue;
2114 }
2115
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002116 char_u *p = line;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002117
2118 char *end_name;
2119 int shortlen;
2120 int fullen;
2121 if (is_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002122 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002123 end_name = "endclass";
2124 shortlen = 4;
2125 fullen = 8;
2126 }
2127 else if (is_enum)
2128 {
2129 end_name = "endenum";
2130 shortlen = 4;
2131 fullen = 7;
2132 }
2133 else
2134 {
2135 end_name = "endinterface";
2136 shortlen = 5;
2137 fullen = 12;
2138 }
2139
2140 if (checkforcmd(&p, end_name, shortlen))
2141 {
2142 if (STRNCMP(line, end_name, fullen) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002143 semsg(_(e_command_cannot_be_shortened_str), line);
2144 else if (*p == '|' || !ends_excmd2(line, p))
2145 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00002146 else
2147 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002148 break;
2149 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002150
2151 int wrong_endname = FALSE;
2152 if (is_class)
2153 wrong_endname = checkforcmd(&p, "endinterface", 5)
2154 || checkforcmd(&p, "endenum", 4);
2155 else if (is_enum)
2156 wrong_endname = checkforcmd(&p, "endclass", 4)
2157 || checkforcmd(&p, "endinterface", 5);
2158 else
2159 wrong_endname = checkforcmd(&p, "endclass", 4)
2160 || checkforcmd(&p, "endenum", 4);
2161 if (wrong_endname)
Bram Moolenaar554d0312023-01-05 19:59:18 +00002162 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00002163 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00002164 break;
2165 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002166
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002167 if (is_enum && !enum_end)
2168 {
2169 // In an enum, all the enumerated values are at the beginning
2170 // separated by comma. The class and object variables/methods
2171 // follow the values.
2172 if (enum_parse_values(eap, cl, line, &classmembers,
2173 &num_enum_values, &enum_end) == FAIL)
2174 break;
Yegappan Lakshmananabedca92024-03-29 10:08:23 +01002175
2176 if (enum_end)
2177 // Add the enum "values" class variable.
2178 enum_add_values_member(cl, &classmembers, num_enum_values,
2179 &type_list);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002180 continue;
2181 }
2182
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002183 int has_public = FALSE;
2184 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002185 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002186 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002187 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002188 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002189 break;
2190 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002191 if (is_interface)
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002192 {
Yegappan Lakshmananb90e3bc2023-09-28 23:06:48 +02002193 emsg(_(e_public_variable_not_supported_in_interface));
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002194 break;
2195 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002196 has_public = TRUE;
2197 p = skipwhite(line + 6);
2198
Yegappan Lakshmananc51578f2024-04-13 17:58:09 +02002199 if (STRNCMP(p, "def", 3) == 0)
2200 {
2201 emsg(_(e_public_keyword_not_supported_for_method));
2202 break;
2203 }
2204
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002205 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "static", 6) != 0
2206 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002207 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002208 emsg(_(e_public_must_be_followed_by_var_static_final_or_const));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002209 break;
2210 }
2211 }
Bram Moolenaard505d172022-12-18 21:42:55 +00002212
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002213 int abstract_method = FALSE;
2214 char_u *pa = p;
2215 if (checkforcmd(&p, "abstract", 3))
2216 {
2217 if (STRNCMP(pa, "abstract", 8) != 0)
2218 {
2219 semsg(_(e_command_cannot_be_shortened_str), pa);
2220 break;
2221 }
2222
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002223 if (is_enum)
2224 {
2225 // "abstract" not supported in an enum
2226 emsg(_(e_abstract_cannot_be_used_in_enum));
2227 break;
2228 }
2229
2230 if (is_interface)
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01002231 {
2232 // "abstract" not supported in an interface
2233 emsg(_(e_abstract_cannot_be_used_in_interface));
2234 break;
2235 }
2236
2237 if (!is_abstract)
2238 {
2239 semsg(_(e_abstract_method_in_concrete_class), pa);
2240 break;
2241 }
2242
Yegappan Lakshmanan5a539252023-11-04 09:42:46 +01002243 p = skipwhite(pa + 8);
2244 if (STRNCMP(p, "def", 3) != 0)
2245 {
2246 emsg(_(e_abstract_must_be_followed_by_def));
2247 break;
2248 }
2249
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01002250 abstract_method = TRUE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002251 }
2252
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002253 int has_static = FALSE;
2254 char_u *ps = p;
2255 if (checkforcmd(&p, "static", 4))
2256 {
2257 if (STRNCMP(ps, "static", 6) != 0)
2258 {
2259 semsg(_(e_command_cannot_be_shortened_str), ps);
2260 break;
2261 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002262
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002263 if (is_interface)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002264 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002265 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002266 break;
2267 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002268 has_static = TRUE;
2269 p = skipwhite(ps + 6);
Doug Kearns74da0ee2023-12-14 20:26:26 +01002270
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002271 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "def", 3) != 0
2272 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Doug Kearns74da0ee2023-12-14 20:26:26 +01002273 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002274 emsg(_(e_static_must_be_followed_by_var_def_final_or_const));
Doug Kearns74da0ee2023-12-14 20:26:26 +01002275 break;
2276 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002277 }
2278
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002279 int has_final = FALSE;
2280 int has_var = FALSE;
2281 int has_const = FALSE;
2282 if (checkforcmd(&p, "var", 3))
2283 has_var = TRUE;
2284 else if (checkforcmd(&p, "final", 5))
2285 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002286 if (is_interface)
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002287 {
2288 emsg(_(e_final_variable_not_supported_in_interface));
2289 break;
2290 }
2291 has_final = TRUE;
2292 }
2293 else if (checkforcmd(&p, "const", 5))
2294 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002295 if (is_interface)
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002296 {
2297 emsg(_(e_const_variable_not_supported_in_interface));
2298 break;
2299 }
2300 has_const = TRUE;
2301 }
2302 p = skipwhite(p);
2303
Bram Moolenaard505d172022-12-18 21:42:55 +00002304 // object members (public, read access, private):
Doug Kearns74da0ee2023-12-14 20:26:26 +01002305 // "var _varname"
2306 // "var varname"
2307 // "public var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002308 // "final _varname"
2309 // "final varname"
2310 // "public final varname"
2311 // "const _varname"
2312 // "const varname"
2313 // "public const varname"
Doug Kearns74da0ee2023-12-14 20:26:26 +01002314 // class members (public, read access, private):
2315 // "static var _varname"
2316 // "static var varname"
2317 // "public static var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002318 // "static final _varname"
2319 // "static final varname"
2320 // "public static final varname"
2321 // "static const _varname"
2322 // "static const varname"
2323 // "public static const varname"
2324 if (has_var || has_final || has_const)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002325 {
Doug Kearns74da0ee2023-12-14 20:26:26 +01002326 char_u *varname = p;
Bram Moolenaard505d172022-12-18 21:42:55 +00002327 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00002328 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00002329 char_u *init_expr = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002330 int has_type = FALSE;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002331
Doug Kearns74da0ee2023-12-14 20:26:26 +01002332 if (!eval_isnamec1(*p))
2333 {
2334 if (has_static)
2335 semsg(_(e_invalid_class_variable_declaration_str), line);
2336 else
2337 semsg(_(e_invalid_object_variable_declaration_str), line);
2338 break;
2339 }
2340
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002341 if (is_interface && *varname == '_')
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002342 {
2343 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01002344 semsg(_(e_protected_variable_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002345 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002346 break;
2347 }
2348
Bram Moolenaard505d172022-12-18 21:42:55 +00002349 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002350 &varname_end, &has_type, &type_list, &type,
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002351 !is_interface ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002352 break;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002353
2354 if (is_reserved_varname(varname, varname_end)
2355 || is_duplicate_variable(&classmembers, &objmembers,
2356 varname, varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02002357 {
2358 vim_free(init_expr);
2359 break;
2360 }
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002361 if (add_member(has_static ? &classmembers : &objmembers, varname,
2362 varname_end, has_public, has_final, has_const,
2363 has_type, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00002364 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002365 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002366 break;
2367 }
Bram Moolenaard505d172022-12-18 21:42:55 +00002368 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002369
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002370 // constructors:
2371 // def new()
2372 // enddef
2373 // def newOther()
2374 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002375 // object methods and class functions:
2376 // def SomeMethod()
2377 // enddef
2378 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002379 // enddef
2380 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002381 // def <Tval> someMethod()
2382 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002383 else if (checkforcmd(&p, "def", 3))
2384 {
2385 exarg_T ea;
2386 garray_T lines_to_free;
Yegappan Lakshmanan7e898002025-02-11 22:07:05 +01002387 int is_new = STRNCMP(p, "new", 3) == 0
2388 || STRNCMP(p, "_new", 4) == 0;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002389
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002390 if (has_public)
2391 {
2392 // "public" keyword is not supported when defining an object or
2393 // class method
2394 emsg(_(e_public_keyword_not_supported_for_method));
2395 break;
2396 }
2397
2398 if (*p == NUL)
2399 {
2400 // No method name following def
2401 semsg(_(e_not_valid_command_in_class_str), line);
2402 break;
2403 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002404
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002405 if (is_interface && *p == '_')
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01002406 {
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002407 // private methods are not supported in an interface
2408 semsg(_(e_protected_method_not_supported_in_interface), p);
2409 break;
2410 }
2411
2412 if (has_static && !is_new && SAFE_islower(*p) &&
2413 is_valid_builtin_obj_methodname(p))
2414 {
2415 semsg(_(e_builtin_class_method_not_supported), p);
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01002416 break;
2417 }
2418
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002419 CLEAR_FIELD(ea);
2420 ea.cmd = line;
2421 ea.arg = p;
2422 ea.cmdidx = CMD_def;
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002423 ea.ea_getline = eap->ea_getline;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002424 ea.cookie = eap->cookie;
2425
2426 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002427 int class_flags;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002428 if (is_interface)
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002429 class_flags = CF_INTERFACE;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002430 else
2431 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
Bram Moolenaar554d0312023-01-05 19:59:18 +00002432 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02002433 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002434 ga_clear_strings(&lines_to_free);
2435
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002436 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002437 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002438 char_u *name = uf->uf_name;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02002439
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002440 if (is_new && !is_valid_constructor(uf, is_abstract,
2441 has_static))
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002442 {
2443 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01002444 semsg(_(e_protected_method_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002445 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002446 func_clear_free(uf, FALSE);
2447 break;
2448 }
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002449
2450 // check for builtin method
2451 if (!is_new && SAFE_islower(*name) &&
2452 !object_check_builtin_method_sig(uf))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00002453 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02002454 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00002455 break;
2456 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02002457
Bram Moolenaar58b40092023-01-11 15:59:05 +00002458 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002459 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002460 {
2461 success = FALSE;
2462 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02002463 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002464 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00002465
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002466 garray_T *fgap = has_static || is_new
2467 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002468 if (ga_grow(fgap, 1) == OK)
2469 {
2470 if (is_new)
2471 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002472
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002473 if (abstract_method)
2474 uf->uf_flags |= FC_ABSTRACT;
2475
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002476 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
2477 ++fgap->ga_len;
2478 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002479 }
2480 }
2481
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002482 else
2483 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00002484 if (is_class)
2485 semsg(_(e_not_valid_command_in_class_str), line);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002486 else if (is_enum)
2487 semsg(_(e_not_valid_command_in_enum_str), line);
Bram Moolenaar554d0312023-01-05 19:59:18 +00002488 else
2489 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002490 break;
2491 }
2492 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002493
2494 if (theline == NULL && !success && is_enum)
2495 emsg(_(e_missing_endenum));
2496
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002497 vim_free(theline);
2498
Yegappan Lakshmananabedca92024-03-29 10:08:23 +01002499 if (success && is_enum && num_enum_values == 0)
2500 // Empty enum statement. Add an empty "values" class variable
Hirohito Higashi5887cce2025-02-16 16:34:30 +01002501 success = enum_add_values_member(cl, &classmembers, 0, &type_list);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002502
Bram Moolenaar83677162023-01-08 19:54:10 +00002503 /*
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002504 * Check a few things
Bram Moolenaar83677162023-01-08 19:54:10 +00002505 */
2506
2507 // Check the "extends" class is valid.
2508 if (success && extends != NULL)
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002509 success = validate_extends_class(cl, extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00002510 VIM_CLEAR(extends);
2511
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002512 // Check the new object methods to make sure their access (public or
2513 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002514 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002515 success = validate_extends_methods(&objmethods, extends_cl);
2516
2517 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002518 // variables in the extended class lineage. If an interface is extending
2519 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002520 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002521 {
2522 if (is_class)
2523 success = extends_check_dup_members(&objmembers, extends_cl);
2524 else
2525 success = extends_check_intf_var_type(&objmembers, extends_cl);
2526 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002527
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002528 // When extending an abstract class, make sure all the abstract methods in
2529 // the parent class are implemented. If the current class is an abstract
2530 // class, then there is no need for this check.
2531 if (success && !is_abstract && extends_cl != NULL
2532 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002533 success = validate_abstract_class_methods(&classfunctions,
2534 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002535
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +01002536 // Process the "implements" entries
Bram Moolenaar83677162023-01-08 19:54:10 +00002537 // Check all "implements" entries are valid.
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +01002538 garray_T intf_classes_ga;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002539
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +01002540 ga_init2(&intf_classes_ga, sizeof(class_T *), 5);
2541
2542 if (success && ga_impl.ga_len > 0)
2543 success = validate_implements_classes(&ga_impl, &intf_classes_ga,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002544 &objmethods, &objmembers, extends_cl);
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +01002545
2546 // inherit the super class interfaces
2547 if (success && extends_cl != NULL)
2548 success = add_super_class_interfaces(extends_cl, &ga_impl,
2549 &intf_classes_ga);
2550
2551 intf_classes = intf_classes_ga.ga_data;
2552 intf_classes_ga.ga_len = 0;
Bram Moolenaar94674f22023-01-06 18:42:20 +00002553
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002554 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00002555 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002556 success = check_func_arg_names(&classfunctions, &objmethods,
2557 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00002558
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002559 if (success)
2560 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002561 // "endclass" or "endinterface" or "endenum" encountered without any
2562 // failures
Bram Moolenaard505d172022-12-18 21:42:55 +00002563
Bram Moolenaard0200c82023-01-28 15:19:40 +00002564 if (extends_cl != NULL)
2565 {
2566 cl->class_extends = extends_cl;
2567 extends_cl->class_flags |= CLASS_EXTENDED;
2568 }
Bram Moolenaar83677162023-01-08 19:54:10 +00002569
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002570 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00002571 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002572 NULL,
2573 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00002574 &cl->class_class_members,
2575 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00002576 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00002577 extends_cl == NULL ? NULL
2578 : extends_cl->class_obj_members,
2579 extends_cl == NULL ? 0
2580 : extends_cl->class_obj_member_count,
2581 &cl->class_obj_members,
2582 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002583 goto cleanup;
2584
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00002585 if (ga_impl.ga_len > 0)
2586 {
2587 // Move the "implements" names into the class.
2588 cl->class_interface_count = ga_impl.ga_len;
2589 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
2590 if (cl->class_interfaces == NULL)
2591 goto cleanup;
2592 for (int i = 0; i < ga_impl.ga_len; ++i)
2593 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
2594 VIM_CLEAR(ga_impl.ga_data);
2595 ga_impl.ga_len = 0;
2596
Bram Moolenaard0200c82023-01-28 15:19:40 +00002597 cl->class_interfaces_cl = intf_classes;
2598 intf_classes = NULL;
2599 }
2600
2601 if (cl->class_interface_count > 0 || extends_cl != NULL)
2602 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002603 // Add a method and member lookup table to each of the interface
2604 // classes.
2605 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
2606 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00002607 }
2608
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002609 int have_new = FALSE;
2610 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002611 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002612 {
2613 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
Yegappan Lakshmanan7e898002025-02-11 22:07:05 +01002614 if (STRCMP(class_func->uf_name, "new") == 0
2615 || STRCMP(class_func->uf_name, "_new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002616 {
2617 have_new = TRUE;
2618 break;
2619 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002620 }
2621
2622 if (have_new)
2623 // The return type of new() is an object of class "cl"
2624 class_func->uf_ret_type->tt_class = cl;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002625 else if ((is_class || is_enum) && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002626 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002627 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002628
Bram Moolenaar58b40092023-01-11 15:59:05 +00002629 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002630 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
2631 &objmethods) == FAIL)
2632 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002633
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002634 update_builtin_method_index(cl);
2635
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002636 class_created(cl);
2637
2638 // Allocate a typval for each class member and initialize it.
2639 if ((is_class || is_enum) && cl->class_class_member_count > 0)
2640 if (add_class_members(cl, eap, &type_list) == FAIL)
2641 goto cleanup;
2642
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002643 cl->class_type_list = type_list;
2644
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002645 if (is_enum)
2646 {
2647 // clear the constructor method names, so that an enum class cannot
2648 // be instantiated
2649 enum_clear_constructors(cl);
2650 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002651
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002652 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00002653 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002654
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002655 return;
2656 }
2657
2658cleanup:
Bram Moolenaar83677162023-01-08 19:54:10 +00002659 vim_free(extends);
2660 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002661
2662 if (intf_classes != NULL)
2663 {
2664 for (int i = 0; i < ga_impl.ga_len; ++i)
2665 class_unref(intf_classes[i]);
2666 vim_free(intf_classes);
2667 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00002668 ga_clear_strings(&ga_impl);
2669
Bram Moolenaard505d172022-12-18 21:42:55 +00002670 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002671 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002672 garray_T *gap = round == 1 ? &classmembers : &objmembers;
2673 if (gap->ga_len == 0 || gap->ga_data == NULL)
2674 continue;
2675
2676 for (int i = 0; i < gap->ga_len; ++i)
2677 {
2678 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2679 vim_free(m->ocm_name);
2680 vim_free(m->ocm_init);
2681 }
2682 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002683 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002684
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002685 for (int i = 0; i < objmethods.ga_len; ++i)
2686 {
2687 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2688 func_clear_free(uf, FALSE);
2689 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002690 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002691
2692 for (int i = 0; i < classfunctions.ga_len; ++i)
2693 {
2694 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2695 func_clear_free(uf, FALSE);
2696 }
2697 ga_clear(&classfunctions);
2698
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002699 clear_type_list(&type_list);
2700}
2701
2702/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002703 * Find member "name" in class "cl", set "member_idx" to the member index and
2704 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002705 * When "is_object" is TRUE, then look for object members. Otherwise look for
2706 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002707 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002708 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002709 */
2710 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002711oc_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002712 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002713 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002714 char_u *name,
2715 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002716 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002717{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002718 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002719 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002720
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002721 *member_idx = -1; // not found (yet)
2722
2723 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2724 member_idx);
2725 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002726 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002727 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2728 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002729 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002730 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002731
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002732 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002733}
2734
2735/*
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002736 * Given a class or object variable index, return the variable type
2737 */
2738 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002739oc_member_type_by_idx(
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002740 class_T *cl,
2741 int is_object,
2742 int member_idx)
2743{
2744 ocmember_T *m;
2745 int member_count;
2746
2747 if (is_object)
2748 {
2749 m = cl->class_obj_members;
2750 member_count = cl->class_obj_member_count;
2751 }
2752 else
2753 {
2754 m = cl->class_class_members;
2755 member_count = cl->class_class_member_count;
2756 }
2757
2758 if (member_idx >= member_count)
2759 return NULL;
2760
2761 return m[member_idx].ocm_type;
2762}
2763
2764/*
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002765 * Type aliases (:type)
2766 */
2767
Yegappan Lakshmanana04003a2024-10-27 21:54:11 +01002768 static void
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002769typealias_free(typealias_T *ta)
2770{
2771 // ta->ta_type is freed in clear_type_list()
2772 vim_free(ta->ta_name);
2773 vim_free(ta);
2774}
2775
2776 void
2777typealias_unref(typealias_T *ta)
2778{
2779 if (ta != NULL && --ta->ta_refcount <= 0)
2780 typealias_free(ta);
2781}
2782
2783/*
2784 * Handle ":type". Create an alias for a type specification.
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002785 */
2786 void
Dominique Pellé0268ff32024-07-28 21:12:20 +02002787ex_type(exarg_T *eap)
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002788{
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002789 char_u *arg = eap->arg;
2790
2791 if (!current_script_is_vim9()
2792 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002793 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002794 {
2795 emsg(_(e_type_can_only_be_defined_in_vim9_script));
2796 return;
2797 }
2798
2799 if (*arg == NUL)
2800 {
2801 emsg(_(e_missing_typealias_name));
2802 return;
2803 }
2804
2805 if (!ASCII_ISUPPER(*arg))
2806 {
2807 semsg(_(e_type_name_must_start_with_uppercase_letter_str), arg);
2808 return;
2809 }
2810
2811 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
2812 if (!IS_WHITE_OR_NUL(*name_end))
2813 {
2814 semsg(_(e_white_space_required_after_name_str), arg);
2815 return;
2816 }
2817 char_u *name_start = arg;
2818
2819 arg = skipwhite(name_end);
2820 if (*arg != '=')
2821 {
2822 semsg(_(e_missing_equal_str), arg);
2823 return;
2824 }
2825 if (!IS_WHITE_OR_NUL(*(arg + 1)))
2826 {
2827 semsg(_(e_white_space_required_after_str_str), "=", arg);
2828 return;
2829 }
2830 arg++;
2831 arg = skipwhite(arg);
2832
2833 if (*arg == NUL)
2834 {
2835 emsg(_(e_missing_typealias_type));
2836 return;
2837 }
2838
2839 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
2840 type_T *type = parse_type(&arg, &si->sn_type_list, TRUE);
2841 if (type == NULL)
2842 return;
2843
2844 if (*arg != NUL)
2845 {
2846 // some text after the type
2847 semsg(_(e_trailing_characters_str), arg);
2848 return;
2849 }
2850
2851 int cc = *name_end;
2852 *name_end = NUL;
2853
2854 typval_T tv;
2855 tv.v_type = VAR_UNKNOWN;
2856 if (eval_variable_import(name_start, &tv) == OK)
2857 {
2858 if (tv.v_type == VAR_TYPEALIAS)
2859 semsg(_(e_typealias_already_exists_for_str), name_start);
2860 else
2861 semsg(_(e_redefining_script_item_str), name_start);
2862 clear_tv(&tv);
2863 goto done;
2864 }
2865
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02002866 // Create a script-local variable for the type alias.
2867 if (type->tt_type != VAR_OBJECT)
2868 {
2869 tv.v_type = VAR_TYPEALIAS;
2870 tv.v_lock = 0;
2871 tv.vval.v_typealias = ALLOC_CLEAR_ONE(typealias_T);
2872 ++tv.vval.v_typealias->ta_refcount;
2873 tv.vval.v_typealias->ta_name = vim_strsave(name_start);
2874 tv.vval.v_typealias->ta_type = type;
2875 }
2876 else
2877 {
2878 // When creating a type alias for a class, use the class type itself to
2879 // create the type alias variable. This is needed to use the type
2880 // alias to invoke class methods (e.g. new()) and use class variables.
2881 tv.v_type = VAR_CLASS;
2882 tv.v_lock = 0;
2883 tv.vval.v_class = type->tt_class;
2884 ++tv.vval.v_class->class_refcount;
2885 }
Yegappan Lakshmanane3fed482025-02-20 22:20:54 +01002886 set_var_const(name_start, current_sctx.sc_sid, NULL, &tv, FALSE,
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002887 ASSIGN_CONST | ASSIGN_FINAL, 0);
2888
2889done:
2890 *name_end = cc;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002891}
2892
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002893/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002894 * Returns OK if a member variable named "name" is present in the class "cl".
2895 * Otherwise returns FAIL. If found, the member variable typval is set in
2896 * "rettv". If "is_object" is TRUE, then the object member variable table is
2897 * searched. Otherwise the class member variable table is searched.
2898 */
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002899 int
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002900get_member_tv(
2901 class_T *cl,
2902 int is_object,
2903 char_u *name,
2904 size_t namelen,
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002905 class_T *current_class,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002906 typval_T *rettv)
2907{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002908 ocmember_T *m;
2909 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002910
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002911 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2912 &m_idx);
2913 if (m == NULL)
2914 return FAIL;
2915
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002916 if (*name == '_' && (current_class == NULL ||
2917 !class_instance_of(current_class, cl)))
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002918 {
Ernie Rael03042a22023-11-11 08:53:32 +01002919 emsg_var_cl_define(e_cannot_access_protected_variable_str,
Ernie Raele6c9aa52023-10-06 19:55:52 +02002920 m->ocm_name, 0, cl);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002921 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002922 }
2923
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002924 if (is_object)
2925 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002926 // The object only contains a pointer to the class, the member values
2927 // array follows right after that.
2928 object_T *obj = rettv->vval.v_object;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002929 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2930 copy_tv(tv, rettv);
Yegappan Lakshmanan41cddfa2025-05-03 19:11:45 +02002931 set_tv_type(rettv, m->ocm_type);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002932 object_unref(obj);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002933 }
2934 else
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002935 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002936 copy_tv(&cl->class_members_tv[m_idx], rettv);
Yegappan Lakshmanan41cddfa2025-05-03 19:11:45 +02002937 set_tv_type(rettv, m->ocm_type);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002938 class_unref(cl);
2939 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002940
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002941 return OK;
2942}
2943
2944/*
2945 * Call an object or class method "name" in class "cl". The method return
2946 * value is returned in "rettv".
2947 */
2948 static int
2949call_oc_method(
2950 class_T *cl,
2951 char_u *name,
2952 size_t len,
2953 char_u *name_end,
2954 evalarg_T *evalarg,
2955 char_u **arg,
2956 typval_T *rettv)
2957{
2958 ufunc_T *fp;
2959 typval_T argvars[MAX_FUNC_ARGS + 1];
2960 int argcount = 0;
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002961 ocmember_T *ocm = NULL;
2962 int m_idx;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002963
2964 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
2965 if (fp == NULL)
2966 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002967 // could be an object or class funcref variable
2968 ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
2969 if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
2970 {
2971 method_not_found_msg(cl, rettv->v_type, name, len);
2972 return FAIL;
2973 }
2974
Yegappan Lakshmanan3e336502024-04-04 19:35:59 +02002975 if (*name == '_')
2976 {
2977 // Protected object or class funcref variable
2978 semsg(_(e_cannot_access_protected_variable_str), ocm->ocm_name,
2979 cl->class_name);
2980 return FAIL;
2981 }
2982
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002983 if (rettv->v_type == VAR_OBJECT)
2984 {
2985 // funcref object variable
2986 object_T *obj = rettv->vval.v_object;
2987 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2988 copy_tv(tv, rettv);
2989 }
2990 else
2991 // funcref class variable
2992 copy_tv(&cl->class_members_tv[m_idx], rettv);
2993 *arg = name_end;
2994 return OK;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002995 }
2996
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002997 if (ocm == NULL && *fp->uf_name == '_')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002998 {
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002999 // Cannot access a protected method outside of a class
Ernie Rael03042a22023-11-11 08:53:32 +01003000 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003001 return FAIL;
3002 }
3003
3004 char_u *argp = name_end;
Ernie Raelb077b582023-12-14 20:11:44 +01003005 int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount, FALSE);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003006 if (ret == FAIL)
3007 return FAIL;
3008
3009 funcexe_T funcexe;
3010 CLEAR_FIELD(funcexe);
3011 funcexe.fe_evaluate = TRUE;
3012 if (rettv->v_type == VAR_OBJECT)
3013 {
3014 funcexe.fe_object = rettv->vval.v_object;
3015 ++funcexe.fe_object->obj_refcount;
3016 }
3017
3018 // Clear the class or object after calling the function, in
3019 // case the refcount is one.
3020 typval_T tv_tofree = *rettv;
3021 rettv->v_type = VAR_UNKNOWN;
3022
3023 // Call the user function. Result goes into rettv;
3024 int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
3025 NULL);
3026
3027 // Clear the previous rettv and the arguments.
3028 clear_tv(&tv_tofree);
3029 for (int idx = 0; idx < argcount; ++idx)
3030 clear_tv(&argvars[idx]);
3031
3032 if (error != FCERR_NONE)
3033 {
3034 user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
3035 return FAIL;
3036 }
3037 *arg = argp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003038
3039 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003040}
3041
3042/*
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003043 * Create a partial typval for "obj.obj_method" and store it in "rettv".
3044 * Returns OK on success and FAIL on memory allocation failure.
3045 */
3046 int
3047obj_method_to_partial_tv(object_T *obj, ufunc_T *obj_method, typval_T *rettv)
3048{
3049 partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
3050 if (pt == NULL)
3051 return FAIL;
3052
3053 pt->pt_refcount = 1;
3054 if (obj != NULL)
3055 {
3056 pt->pt_obj = obj;
3057 ++pt->pt_obj->obj_refcount;
3058 }
3059 pt->pt_auto = TRUE;
3060 pt->pt_func = obj_method;
3061 func_ptr_ref(pt->pt_func);
3062
3063 rettv->v_type = VAR_PARTIAL;
3064 rettv->vval.v_partial = pt;
3065
3066 return OK;
3067}
3068
3069/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003070 * Evaluate what comes after a class:
3071 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003072 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003073 * - class constructor: SomeClass.new()
3074 * - object member: someObject.varname
3075 * - object method: someObject.SomeMethod()
3076 *
3077 * "*arg" points to the '.'.
3078 * "*arg" is advanced to after the member name or method call.
3079 *
3080 * Returns FAIL or OK.
3081 */
3082 int
3083class_object_index(
3084 char_u **arg,
3085 typval_T *rettv,
3086 evalarg_T *evalarg,
3087 int verbose UNUSED) // give error messages
3088{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003089 if (VIM_ISWHITE((*arg)[1]))
3090 {
3091 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
3092 return FAIL;
3093 }
3094
3095 ++*arg;
3096 char_u *name = *arg;
3097 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
3098 if (name_end == name)
3099 return FAIL;
3100 size_t len = name_end - name;
3101
Ernie Raeld615a312023-10-05 20:28:16 +02003102 int did_emsg_save = did_emsg;
Bram Moolenaar552bdca2023-02-17 21:08:50 +00003103 class_T *cl;
3104 if (rettv->v_type == VAR_CLASS)
3105 cl = rettv->vval.v_class;
3106 else // VAR_OBJECT
3107 {
3108 if (rettv->vval.v_object == NULL)
3109 {
3110 emsg(_(e_using_null_object));
3111 return FAIL;
3112 }
3113 cl = rettv->vval.v_object->obj_class;
3114 }
3115
Bram Moolenaard13dd302023-03-11 20:56:35 +00003116 if (cl == NULL)
3117 {
3118 emsg(_(e_incomplete_type));
3119 return FAIL;
3120 }
3121
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003122 if (*name_end == '(')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003123 // Invoke the class or object method
3124 return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003125
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003126 else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003127 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003128 // Search in the object member variable table and the class member
3129 // variable table.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003130 int is_object = rettv->v_type == VAR_OBJECT;
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003131 if (get_member_tv(cl, is_object, name, len, NULL, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003132 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003133 *arg = name_end;
3134 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003135 }
3136
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003137 // could be a class method or an object method
3138 int fidx;
3139 ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
3140 if (fp != NULL)
3141 {
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003142 // Protected methods are not accessible outside the class
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003143 if (*name == '_')
3144 {
Ernie Rael03042a22023-11-11 08:53:32 +01003145 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003146 return FAIL;
3147 }
3148
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003149 if (obj_method_to_partial_tv(is_object ? rettv->vval.v_object :
3150 NULL, fp, rettv) == FAIL)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003151 return FAIL;
3152
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003153 *arg = name_end;
3154 return OK;
3155 }
3156
Ernie Raeld615a312023-10-05 20:28:16 +02003157 if (did_emsg == did_emsg_save)
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +02003158 member_not_found_msg(cl, rettv->v_type, name, len);
Bram Moolenaard505d172022-12-18 21:42:55 +00003159 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003160
3161 return FAIL;
3162}
3163
3164/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003165 * If "arg" points to a class or object method, return it.
3166 * Otherwise return NULL.
3167 */
3168 ufunc_T *
3169find_class_func(char_u **arg)
3170{
3171 char_u *name = *arg;
3172 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
3173 if (name_end == name || *name_end != '.')
3174 return NULL;
3175
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003176 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003177 size_t len = name_end - name;
3178 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003179 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00003180 if (eval_variable(name, (int)len,
3181 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003182 return NULL;
3183 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00003184 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003185
3186 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
3187 : tv.vval.v_object->obj_class;
3188 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00003189 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003190 char_u *fname = name_end + 1;
3191 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
3192 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00003193 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003194 len = fname_end - fname;
3195
Ernie Rael4d00b832023-09-11 19:54:42 +02003196 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003197
Bram Moolenaareb533502022-12-14 15:06:11 +00003198fail_after_eval:
3199 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003200 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003201}
3202
3203/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003204 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003205 * Returns -1, if the variable is not found.
3206 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003207 */
3208 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003209class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003210{
Ernie Rael4d00b832023-09-11 19:54:42 +02003211 int idx;
3212 class_member_lookup(cl, name, namelen, &idx);
3213 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003214}
3215
3216/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003217 * Returns a pointer to the class member variable "name" in the class "cl".
3218 * Returns NULL if the variable is not found.
3219 * The member variable index is set in "idx".
3220 */
3221 ocmember_T *
3222class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3223{
Ernie Rael4d00b832023-09-11 19:54:42 +02003224 ocmember_T *ret_m = NULL;
3225 int ret_idx = -1;
3226 for (int i = 0; i < cl->class_class_member_count; ++i)
3227 {
3228 ocmember_T *m = &cl->class_class_members[i];
3229 if (namelen)
3230 {
3231 if (STRNCMP(name, m->ocm_name, namelen) == 0
3232 && m->ocm_name[namelen] == NUL)
3233 {
3234 ret_m = m;
3235 ret_idx = i;
3236 break;
3237 }
3238 }
3239 else if (STRCMP(name, m->ocm_name) == 0)
3240 {
3241 ret_m = m;
3242 ret_idx = i;
zeertzjqd9be94c2024-07-14 10:20:20 +02003243 break;
Ernie Rael4d00b832023-09-11 19:54:42 +02003244 }
3245 }
3246 if (idx != NULL)
3247 *idx = ret_idx;
3248 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003249}
3250
3251/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003252 * Returns a pointer to the class method "name" in class "cl".
3253 * Returns NULL if the method is not found.
3254 * The method index is set in "idx".
3255 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003256 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003257class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3258{
Ernie Rael4d00b832023-09-11 19:54:42 +02003259 ufunc_T *ret_fp = NULL;
3260 int ret_idx = -1;
3261 for (int i = 0; i < cl->class_class_function_count; ++i)
3262 {
3263 ufunc_T *fp = cl->class_class_functions[i];
3264 char_u *ufname = (char_u *)fp->uf_name;
3265 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
3266 {
3267 ret_fp = fp;
3268 ret_idx = i;
3269 break;
3270 }
3271 }
3272 if (idx != NULL)
3273 *idx = ret_idx;
3274 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003275}
3276
3277/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003278 * Returns the index of class method "name" in the class "cl".
3279 * Returns -1, if the method is not found.
3280 */
3281 int
3282class_method_idx(class_T *cl, char_u *name, size_t namelen)
3283{
3284 int idx;
3285 class_method_lookup(cl, name, namelen, &idx);
3286 return idx;
3287}
3288
3289/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003290 * Returns the index of object member variable "name" in the class "cl".
3291 * Returns -1, if the variable is not found.
3292 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
3293 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003294 static int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003295object_member_idx(class_T *cl, char_u *name, size_t namelen)
3296{
Ernie Rael4d00b832023-09-11 19:54:42 +02003297 int idx;
3298 object_member_lookup(cl, name, namelen, &idx);
3299 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02003300}
3301
3302/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003303 * Returns a pointer to the object member variable "name" in the class "cl".
3304 * Returns NULL if the variable is not found.
3305 * The object member variable index is set in "idx".
3306 */
3307 ocmember_T *
3308object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3309{
Ernie Rael4d00b832023-09-11 19:54:42 +02003310 ocmember_T *ret_m = NULL;
3311 int ret_idx = -1;
3312 for (int i = 0; i < cl->class_obj_member_count; ++i)
3313 {
3314 ocmember_T *m = &cl->class_obj_members[i];
3315 if (namelen)
3316 {
3317 if (STRNCMP(name, m->ocm_name, namelen) == 0
3318 && m->ocm_name[namelen] == NUL)
3319 {
3320 ret_m = m;
3321 ret_idx = i;
3322 break;
3323 }
3324 }
3325 else if (STRCMP(name, m->ocm_name) == 0)
zeertzjqd9be94c2024-07-14 10:20:20 +02003326 {
Ernie Rael4d00b832023-09-11 19:54:42 +02003327 ret_m = m;
3328 ret_idx = i;
zeertzjqd9be94c2024-07-14 10:20:20 +02003329 break;
3330 }
Ernie Rael4d00b832023-09-11 19:54:42 +02003331 }
3332 if (idx != NULL)
3333 *idx = ret_idx;
3334 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003335}
3336
3337/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003338 * Returns a pointer to the object method "name" in class "cl".
3339 * Returns NULL if the method is not found.
3340 * The object method index is set in "idx".
3341 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003342 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003343object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3344{
Ernie Rael4d00b832023-09-11 19:54:42 +02003345 ufunc_T *ret_fp = NULL;
3346 int ret_idx = -1;
3347 for (int i = 0; i < cl->class_obj_method_count; ++i)
3348 {
3349 ufunc_T *fp = cl->class_obj_methods[i];
3350 // Use a separate pointer to avoid that ASAN complains about
3351 // uf_name[] only being 4 characters.
3352 char_u *ufname = (char_u *)fp->uf_name;
3353 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
3354 {
3355 ret_fp = fp;
3356 ret_idx = i;
3357 break;
3358 }
3359 }
3360 if (idx != NULL)
3361 *idx = ret_idx;
3362 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003363}
3364
3365/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003366 * Returns the index of object method "name" in the class "cl".
3367 * Returns -1, if the method is not found.
3368 */
3369 int
3370object_method_idx(class_T *cl, char_u *name, size_t namelen)
3371{
3372 int idx;
3373 object_method_lookup(cl, name, namelen, &idx);
3374 return idx;
3375}
3376
3377/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003378 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
3379 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
3380 * object member variable.
3381 *
3382 * Returns a pointer to the member variable structure if variable is found.
3383 * Otherwise returns NULL. The member variable index is set in "*idx".
3384 */
3385 ocmember_T *
3386member_lookup(
3387 class_T *cl,
3388 vartype_T v_type,
3389 char_u *name,
3390 size_t namelen,
3391 int *idx)
3392{
3393 if (v_type == VAR_CLASS)
3394 return class_member_lookup(cl, name, namelen, idx);
3395 else
3396 return object_member_lookup(cl, name, namelen, idx);
3397}
3398
3399/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003400 * Find the class that defines the named member. Look up the hierarchy
3401 * starting at "cl".
3402 *
3403 * Return the class that defines the member "name", else NULL.
3404 * Fill in "p_m", if specified, for ocmember_T in found class.
3405 */
3406// NOTE: if useful for something could also indirectly return vartype and idx.
3407 static class_T *
3408class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m)
3409{
3410 class_T *cl_found = NULL;
3411 vartype_T vartype = VAR_UNKNOWN;
3412 ocmember_T *m_found = NULL;
3413
3414 len = len != 0 ? len : STRLEN(name);
3415
3416 // Loop assumes if member is not defined in "cl", then it is not
3417 // defined in any super class; the last class where it's found is the
3418 // class where it is defined. Once the vartype is found, the other
3419 // type is no longer checked.
3420 for (class_T *super = cl; super != NULL; super = super->class_extends)
3421 {
3422 class_T *cl_tmp = NULL;
3423 ocmember_T *m = NULL;
3424 if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT)
3425 {
3426 if ((m = object_member_lookup(super, name, len, NULL)) != NULL)
3427 {
3428 cl_tmp = super;
3429 vartype = VAR_OBJECT;
3430 }
3431 }
3432 if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS)
3433 {
3434 if (( m = class_member_lookup(super, name, len, NULL)) != NULL)
3435 {
3436 cl_tmp = super;
Zdenek Dohnal215c82d2024-12-04 20:19:40 +01003437 vartype = VAR_CLASS;
Ernie Raele6c9aa52023-10-06 19:55:52 +02003438 }
3439 }
3440 if (cl_tmp == NULL)
3441 break; // member is not in this or any super class.
3442 cl_found = cl_tmp;
3443 m_found = m;
3444 }
3445 if (p_m != NULL)
3446 *p_m = m_found;
3447 return cl_found;
3448}
3449
3450/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003451 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
3452 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
3453 *
3454 * Returns a pointer to the method structure if variable is found.
3455 * Otherwise returns NULL. The method variable index is set in "*idx".
3456 */
3457 ufunc_T *
3458method_lookup(
3459 class_T *cl,
3460 vartype_T v_type,
3461 char_u *name,
3462 size_t namelen,
3463 int *idx)
3464{
3465 if (v_type == VAR_CLASS)
3466 return class_method_lookup(cl, name, namelen, idx);
3467 else
3468 return object_method_lookup(cl, name, namelen, idx);
3469}
3470
3471/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00003472 * Return TRUE if current context "cctx_arg" is inside class "cl".
3473 * Return FALSE if not.
3474 */
3475 int
3476inside_class(cctx_T *cctx_arg, class_T *cl)
3477{
3478 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02003479 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003480 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00003481 return TRUE;
3482 return FALSE;
3483}
3484
3485/*
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01003486 * Return TRUE if object/class variable "m" is read-only.
3487 * Also give an error message.
3488 */
3489 int
3490oc_var_check_ro(class_T *cl, ocmember_T *m)
3491{
3492 if (m->ocm_flags & (OCMFLAG_FINAL | OCMFLAG_CONST))
3493 {
3494 semsg(_(e_cannot_change_readonly_variable_str_in_class_str),
3495 m->ocm_name, cl->class_name);
3496 return TRUE;
3497 }
3498 return FALSE;
3499}
3500
3501/*
3502 * Lock all the constant object variables. Called after creating and
3503 * initializing a new object.
3504 */
3505 void
3506obj_lock_const_vars(object_T *obj)
3507{
3508 for (int i = 0; i < obj->obj_class->class_obj_member_count; i++)
3509 {
3510 ocmember_T *ocm = &obj->obj_class->class_obj_members[i];
3511 if (ocm->ocm_flags & OCMFLAG_CONST)
3512 {
3513 typval_T *mtv = ((typval_T *)(obj + 1)) + i;
3514 item_lock(mtv, DICT_MAXNEST, TRUE, TRUE);
3515 }
3516 }
3517}
3518
3519/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003520 * Make a copy of an object.
3521 */
3522 void
3523copy_object(typval_T *from, typval_T *to)
3524{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003525 if (from->vval.v_object == NULL)
3526 to->vval.v_object = NULL;
3527 else
3528 {
3529 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003530 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003531 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003532}
3533
3534/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003535 * Make a copy of a class.
3536 */
3537 void
3538copy_class(typval_T *from, typval_T *to)
3539{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003540 if (from->vval.v_class == NULL)
3541 to->vval.v_class = NULL;
3542 else
3543 {
3544 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003545 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003546 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003547}
3548
3549/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003550 * Free the class "cl" and its contents.
3551 */
3552 static void
3553class_free(class_T *cl)
3554{
3555 // Freeing what the class contains may recursively come back here.
3556 // Clear "class_name" first, if it is NULL the class does not need to
3557 // be freed.
3558 VIM_CLEAR(cl->class_name);
3559
3560 class_unref(cl->class_extends);
3561
3562 for (int i = 0; i < cl->class_interface_count; ++i)
3563 {
3564 vim_free(((char_u **)cl->class_interfaces)[i]);
3565 if (cl->class_interfaces_cl[i] != NULL)
3566 class_unref(cl->class_interfaces_cl[i]);
3567 }
3568 vim_free(cl->class_interfaces);
3569 vim_free(cl->class_interfaces_cl);
3570
3571 itf2class_T *next;
3572 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
3573 {
3574 next = i2c->i2c_next;
3575 vim_free(i2c);
3576 }
3577
3578 for (int i = 0; i < cl->class_class_member_count; ++i)
3579 {
3580 ocmember_T *m = &cl->class_class_members[i];
3581 vim_free(m->ocm_name);
3582 vim_free(m->ocm_init);
3583 if (cl->class_members_tv != NULL)
3584 clear_tv(&cl->class_members_tv[i]);
3585 }
3586 vim_free(cl->class_class_members);
3587 vim_free(cl->class_members_tv);
3588
3589 for (int i = 0; i < cl->class_obj_member_count; ++i)
3590 {
3591 ocmember_T *m = &cl->class_obj_members[i];
3592 vim_free(m->ocm_name);
3593 vim_free(m->ocm_init);
3594 }
3595 vim_free(cl->class_obj_members);
3596
3597 for (int i = 0; i < cl->class_class_function_count; ++i)
3598 {
3599 ufunc_T *uf = cl->class_class_functions[i];
3600 func_clear_free(uf, FALSE);
3601 }
3602 vim_free(cl->class_class_functions);
3603
3604 for (int i = 0; i < cl->class_obj_method_count; ++i)
3605 {
3606 ufunc_T *uf = cl->class_obj_methods[i];
3607 func_clear_free(uf, FALSE);
3608 }
3609 vim_free(cl->class_obj_methods);
3610
3611 clear_type_list(&cl->class_type_list);
3612
3613 class_cleared(cl);
3614
3615 vim_free(cl);
3616}
3617
3618/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003619 * Unreference a class. Free it when the reference count goes down to zero.
3620 */
3621 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003622class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003623{
Bram Moolenaard505d172022-12-18 21:42:55 +00003624 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003625 class_free(cl);
3626}
3627
3628/*
3629 * Go through the list of all classes and free items without "copyID".
3630 */
3631 int
3632class_free_nonref(int copyID)
3633{
3634 int did_free = FALSE;
3635
3636 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003637 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003638 next_nonref_class = cl->class_next_used;
3639 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003640 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003641 // Free the class and items it contains.
3642 class_free(cl);
3643 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003644 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003645 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003646
3647 next_nonref_class = NULL;
3648 return did_free;
3649}
3650
3651 int
3652set_ref_in_classes(int copyID)
3653{
3654 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01003655 set_ref_in_item_class(cl, copyID, NULL, NULL, NULL);
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003656
3657 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003658}
3659
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003660static object_T *first_object = NULL;
3661
3662/*
3663 * Call this function when an object has been created. It will be added to the
3664 * list headed by "first_object".
3665 */
3666 void
3667object_created(object_T *obj)
3668{
3669 if (first_object != NULL)
3670 {
3671 obj->obj_next_used = first_object;
3672 first_object->obj_prev_used = obj;
3673 }
3674 first_object = obj;
3675}
3676
3677/*
3678 * Call this function when an object has been cleared and is about to be freed.
3679 * It is removed from the list headed by "first_object".
3680 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003681 static void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003682object_cleared(object_T *obj)
3683{
3684 if (obj->obj_next_used != NULL)
3685 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
3686 if (obj->obj_prev_used != NULL)
3687 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
3688 else if (first_object == obj)
3689 first_object = obj->obj_next_used;
3690}
3691
3692/*
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003693 * Free the contents of an object ignoring the reference count.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003694 */
3695 static void
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003696object_free_contents(object_T *obj)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003697{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003698 class_T *cl = obj->obj_class;
3699
3700 if (!cl)
3701 return;
3702
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003703 // Avoid a recursive call, it can happen if "obj" has a circular reference.
3704 obj->obj_refcount = INT_MAX;
3705
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003706 // the member values are just after the object structure
3707 typval_T *tv = (typval_T *)(obj + 1);
3708 for (int i = 0; i < cl->class_obj_member_count; ++i)
3709 clear_tv(tv + i);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003710}
3711
3712 static void
3713object_free_object(object_T *obj)
3714{
3715 class_T *cl = obj->obj_class;
3716
3717 if (!cl)
3718 return;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003719
3720 // Remove from the list headed by "first_object".
3721 object_cleared(obj);
3722
3723 vim_free(obj);
3724 class_unref(cl);
3725}
3726
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003727 static void
3728object_free(object_T *obj)
3729{
3730 if (in_free_unref_items)
3731 return;
3732
3733 object_free_contents(obj);
3734 object_free_object(obj);
3735}
3736
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003737/*
3738 * Unreference an object.
3739 */
3740 void
3741object_unref(object_T *obj)
3742{
3743 if (obj != NULL && --obj->obj_refcount <= 0)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003744 object_free(obj);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003745}
3746
3747/*
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003748 * Go through the list of all objects and free items without "copyID".
3749 */
3750 int
3751object_free_nonref(int copyID)
3752{
3753 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003754
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003755 for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003756 {
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003757 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3758 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003759 // Free the object contents. Object itself will be freed later.
3760 object_free_contents(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003761 did_free = TRUE;
3762 }
3763 }
3764
3765 return did_free;
3766}
3767
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003768 void
3769object_free_items(int copyID)
3770{
3771 object_T *obj_next;
3772
3773 for (object_T *obj = first_object; obj != NULL; obj = obj_next)
3774 {
3775 obj_next = obj->obj_next_used;
3776 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3777 object_free_object(obj);
3778 }
3779}
3780
LemonBoyafe04662023-08-23 21:08:11 +02003781/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003782 * Output message which takes a variable name and the class that defines it.
3783 * "cl" is that class where the name was found. Search "cl"'s hierarchy to
3784 * find the defining class.
3785 */
3786 void
3787emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl)
3788{
3789 ocmember_T *m;
3790 class_T *cl_def = class_defining_member(cl, name, len, &m);
3791 if (cl_def != NULL)
3792 semsg(_(msg), m->ocm_name, cl_def->class_name);
3793 else
3794 emsg(_(e_internal_error_please_report_a_bug));
3795}
3796
3797/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003798 * Echo a class or object method not found message.
3799 */
3800 void
3801method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3802{
3803 char_u *method_name = vim_strnsave(name, len);
3804 if ((v_type == VAR_OBJECT)
3805 && (class_method_idx(cl, name, len) >= 0))
3806 {
3807 // If this is a class method, then give a different error
3808 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003809 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003810 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003811 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003812 method_name, cl->class_name);
3813 }
3814 else if ((v_type == VAR_CLASS)
3815 && (object_method_idx(cl, name, len) >= 0))
3816 {
3817 // If this is an object method, then give a different error
3818 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003819 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003820 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003821 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003822 method_name, cl->class_name);
3823 }
3824 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003825 semsg(_(e_method_not_found_on_class_str_str), method_name,
zeertzjqd9be94c2024-07-14 10:20:20 +02003826 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003827 vim_free(method_name);
3828}
3829
3830/*
3831 * Echo a class or object member not found message.
3832 */
3833 void
3834member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3835{
3836 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
3837
3838 if (v_type == VAR_OBJECT)
3839 {
3840 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003841 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003842 varname, cl->class_name);
3843 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003844 semsg(_(e_variable_not_found_on_object_str_str), varname,
3845 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003846 }
3847 else
3848 {
3849 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003850 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003851 varname, cl->class_name);
3852 else
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01003853 {
3854 if (IS_ENUM(cl))
3855 semsg(_(e_enum_value_str_not_found_in_enum_str),
3856 varname, cl->class_name);
3857 else
3858 semsg(_(e_class_variable_str_not_found_in_class_str),
3859 varname, cl->class_name);
3860 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003861 }
3862 vim_free(varname);
3863}
3864
3865/*
Yegappan Lakshmanan4f32c832024-01-12 17:36:40 +01003866 * Compile all the class and object methods in "cl".
3867 */
3868 void
3869defcompile_class(class_T *cl)
3870{
3871 for (int loop = 1; loop <= 2; ++loop)
3872 {
3873 int func_count = loop == 1 ? cl->class_class_function_count
3874 : cl->class_obj_method_count;
3875 for (int i = 0; i < func_count; i++)
3876 {
3877 ufunc_T *ufunc = loop == 1 ? cl->class_class_functions[i]
3878 : cl->class_obj_methods[i];
Yegappan Lakshmanan1af0fbf2024-04-09 21:39:27 +02003879 // Don't compile abstract methods
3880 if (!IS_ABSTRACT_METHOD(ufunc))
3881 defcompile_function(ufunc, cl);
Yegappan Lakshmanan4f32c832024-01-12 17:36:40 +01003882 }
3883 }
3884}
3885
3886/*
3887 * Compile all the classes defined in the current script
3888 */
3889 void
3890defcompile_classes_in_script(void)
3891{
3892 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3893 {
3894 if (eval_variable(cl->class_name, 0, 0, NULL, NULL,
3895 EVAL_VAR_NOAUTOLOAD | EVAL_VAR_NO_FUNC) != FAIL)
3896 defcompile_class(cl);
3897 }
3898}
3899
3900/*
3901 * Returns TRUE if "name" is the name of a class. The typval for the class is
3902 * returned in "rettv".
3903 */
3904 int
3905is_class_name(char_u *name, typval_T *rettv)
3906{
3907 rettv->v_type = VAR_UNKNOWN;
3908
3909 if (eval_variable(name, 0, 0, rettv, NULL, EVAL_VAR_NOAUTOLOAD |
3910 EVAL_VAR_NO_FUNC) != FAIL)
3911 return rettv->v_type == VAR_CLASS;
3912 return FALSE;
3913}
3914
3915/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003916 * Calls the object builtin method "name" with arguments "argv". The value
3917 * returned by the builtin method is in "rettv". Returns OK or FAIL.
3918 */
3919 static int
3920object_call_builtin_method(
3921 object_T *obj,
3922 class_builtin_T builtin_method,
3923 int argc,
3924 typval_T *argv,
3925 typval_T *rettv)
3926{
3927 ufunc_T *uf;
3928 int midx;
3929
3930 if (obj == NULL)
3931 return FAIL;
3932
3933 uf = class_get_builtin_method(obj->obj_class, builtin_method, &midx);
3934 if (uf == NULL)
3935 return FAIL;
3936
3937 funccall_T *fc = create_funccal(uf, rettv);
3938 int r;
3939
3940 if (fc == NULL)
3941 return FAIL;
3942
3943 ++obj->obj_refcount;
3944
3945 r = call_def_function(uf, argc, argv, 0, NULL, obj, fc, rettv);
3946
3947 remove_funccal();
3948
3949 return r;
3950}
3951
3952/*
zeertzjqc029c132024-03-28 11:37:26 +01003953 * Calls the object "empty()" method and returns the method return value. In
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003954 * case of an error, returns TRUE.
3955 */
3956 int
3957object_empty(object_T *obj)
3958{
3959 typval_T rettv;
3960
3961 if (object_call_builtin_method(obj, CLASS_BUILTIN_EMPTY, 0, NULL, &rettv)
3962 == FAIL)
3963 return TRUE;
3964
3965 return tv_get_bool(&rettv);
3966}
3967
3968/*
3969 * Use the object "len()" method to get an object length. Returns 0 if the
3970 * method is not found or there is an error.
3971 */
3972 int
3973object_len(object_T *obj)
3974{
3975 typval_T rettv;
3976
3977 if (object_call_builtin_method(obj, CLASS_BUILTIN_LEN, 0, NULL, &rettv)
3978 == FAIL)
3979 return 0;
3980
3981 return tv_to_number(&rettv);
3982}
3983
3984/*
LemonBoy7b29cc92024-06-22 17:25:07 +02003985 * Return TRUE when two objects have exactly the same values.
3986 */
3987 int
3988object_equal(
3989 object_T *o1,
3990 object_T *o2,
Yinzuo Jiang7ccd1a22024-07-04 17:20:53 +02003991 int ic) // ignore case for strings
LemonBoy7b29cc92024-06-22 17:25:07 +02003992{
3993 class_T *cl1, *cl2;
3994
3995 if (o1 == o2)
3996 return TRUE;
Ernie Rael86257142024-06-23 09:54:45 +02003997 if (o1 == NULL || o2 == NULL)
3998 return FALSE;
LemonBoy7b29cc92024-06-22 17:25:07 +02003999
4000 cl1 = o1->obj_class;
4001 cl2 = o2->obj_class;
4002
4003 if (cl1 != cl2 || cl1 == NULL || cl2 == NULL)
4004 return FALSE;
4005
4006 for (int i = 0; i < cl1->class_obj_member_count; ++i)
Yinzuo Jiang7ccd1a22024-07-04 17:20:53 +02004007 if (!tv_equal((typval_T *)(o1 + 1) + i, (typval_T *)(o2 + 1) + i, ic))
LemonBoy7b29cc92024-06-22 17:25:07 +02004008 return FALSE;
4009
4010 return TRUE;
4011}
4012
4013/*
Ernie Rael05ff4e42024-07-04 16:50:11 +02004014 * Return a textual representation of object "obj".
4015 * "obj" must not be NULL.
4016 * May return NULL.
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004017 */
4018 char_u *
Yegappan Lakshmanan22029ed2024-05-20 13:57:11 +02004019object2string(
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004020 object_T *obj,
4021 char_u *numbuf,
4022 int copyID,
4023 int echo_style,
4024 int restore_copyID,
4025 int composite_val)
4026{
4027 typval_T rettv;
4028
4029 if (object_call_builtin_method(obj, CLASS_BUILTIN_STRING, 0, NULL, &rettv)
4030 == OK
4031 && rettv.vval.v_string != NULL)
4032 return rettv.vval.v_string;
Ernie Rael05ff4e42024-07-04 16:50:11 +02004033
4034 int ok = OK;
4035 class_T *cl = obj->obj_class;
4036 garray_T ga;
4037 ga_init2(&ga, 1, 50);
4038
4039 if (cl != NULL && IS_ENUM(cl))
4040 {
4041 ga_concat(&ga, (char_u *)"enum ");
4042 ga_concat(&ga, cl->class_name);
4043 char_u *enum_name = ((typval_T *)(obj + 1))->vval.v_string;
4044 ga_concat(&ga, (char_u *)".");
4045 ga_concat(&ga, enum_name);
4046 }
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004047 else
4048 {
Ernie Rael05ff4e42024-07-04 16:50:11 +02004049 ga_concat(&ga, (char_u *)"object of ");
4050 ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]"
4051 : cl->class_name);
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004052 }
Ernie Rael05ff4e42024-07-04 16:50:11 +02004053 if (cl != NULL)
4054 {
4055 ga_concat(&ga, (char_u *)" {");
4056 for (int i = 0; i < cl->class_obj_member_count; ++i)
4057 {
4058 if (i > 0)
4059 ga_concat(&ga, (char_u *)", ");
4060 ocmember_T *m = &cl->class_obj_members[i];
4061 ga_concat(&ga, m->ocm_name);
4062 ga_concat(&ga, (char_u *)": ");
4063 char_u *tf = NULL;
4064 char_u *s = echo_string_core((typval_T *)(obj + 1) + i,
4065 &tf, numbuf, copyID, echo_style,
4066 restore_copyID, composite_val);
4067 if (s != NULL)
4068 ga_concat(&ga, s);
4069 vim_free(tf);
4070 if (s == NULL || did_echo_string_emsg)
4071 {
4072 ok = FAIL;
4073 break;
4074 }
4075 line_breakcheck();
4076 }
4077 ga_concat(&ga, (char_u *)"}");
4078 }
4079 if (ok == FAIL)
4080 {
4081 vim_free(ga.ga_data);
4082 return NULL;
4083 }
Yegappan Lakshmanancb848b62025-01-20 21:38:09 +01004084 ga_append(&ga, NUL);
Ernie Rael05ff4e42024-07-04 16:50:11 +02004085 return (char_u *)ga.ga_data;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004086}
4087
4088/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02004089 * Return TRUE when the class "cl", its base class or one of the implemented
4090 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02004091 */
4092 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004093class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02004094{
4095 if (cl == other_cl)
4096 return TRUE;
4097
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004098 // Recursively check the base classes.
4099 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02004100 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004101 if (cl == other_cl)
4102 return TRUE;
4103 // Check the implemented interfaces and the super interfaces
4104 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02004105 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004106 class_T *intf = cl->class_interfaces_cl[i];
4107 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02004108 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004109 if (intf == other_cl)
4110 return TRUE;
4111 // check the super interfaces
4112 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02004113 }
4114 }
LemonBoyafe04662023-08-23 21:08:11 +02004115 }
4116
4117 return FALSE;
4118}
4119
4120/*
Ernie Rael2025af12023-12-12 16:58:00 +01004121 * "instanceof(object, classinfo, ...)" function
LemonBoyafe04662023-08-23 21:08:11 +02004122 */
4123 void
4124f_instanceof(typval_T *argvars, typval_T *rettv)
4125{
4126 typval_T *object_tv = &argvars[0];
4127 typval_T *classinfo_tv = &argvars[1];
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02004128 class_T *c;
LemonBoyafe04662023-08-23 21:08:11 +02004129
4130 rettv->vval.v_number = VVAL_FALSE;
4131
4132 if (check_for_object_arg(argvars, 0) == FAIL
Ernie Rael2025af12023-12-12 16:58:00 +01004133 || check_for_class_or_typealias_args(argvars, 1) == FAIL)
LemonBoyafe04662023-08-23 21:08:11 +02004134 return;
4135
Ernie Rael3da696d2023-09-19 20:14:18 +02004136 if (object_tv->vval.v_object == NULL)
Yegappan Lakshmanan6fa62082025-04-03 21:26:34 +02004137 {
4138 if (classinfo_tv->vval.v_class == NULL)
4139 // consider null_object as an instance of null_class
4140 rettv->vval.v_number = VVAL_TRUE;
Ernie Rael3da696d2023-09-19 20:14:18 +02004141 return;
Yegappan Lakshmanan6fa62082025-04-03 21:26:34 +02004142 }
Ernie Rael3da696d2023-09-19 20:14:18 +02004143
Ernie Rael2025af12023-12-12 16:58:00 +01004144 for (; classinfo_tv->v_type != VAR_UNKNOWN; ++classinfo_tv)
LemonBoyafe04662023-08-23 21:08:11 +02004145 {
Ernie Rael2025af12023-12-12 16:58:00 +01004146 if (classinfo_tv->v_type == VAR_TYPEALIAS)
4147 c = classinfo_tv->vval.v_typealias->ta_type->tt_class;
4148 else
4149 c = classinfo_tv->vval.v_class;
4150
4151 if (class_instance_of(object_tv->vval.v_object->obj_class, c))
LemonBoyafe04662023-08-23 21:08:11 +02004152 {
Ernie Rael2025af12023-12-12 16:58:00 +01004153 rettv->vval.v_number = VVAL_TRUE;
4154 return;
LemonBoyafe04662023-08-23 21:08:11 +02004155 }
4156 }
LemonBoyafe04662023-08-23 21:08:11 +02004157}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00004158
4159#endif // FEAT_EVAL