blob: 560c5ae283498d6c8e721c5fdf5708954cfbb99f [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 Lakshmananb8523052023-10-08 19:07:39 +02002931 object_unref(obj);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002932 }
2933 else
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002934 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002935 copy_tv(&cl->class_members_tv[m_idx], rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002936 class_unref(cl);
2937 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002938
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002939 return OK;
2940}
2941
2942/*
2943 * Call an object or class method "name" in class "cl". The method return
2944 * value is returned in "rettv".
2945 */
2946 static int
2947call_oc_method(
2948 class_T *cl,
2949 char_u *name,
2950 size_t len,
2951 char_u *name_end,
2952 evalarg_T *evalarg,
2953 char_u **arg,
2954 typval_T *rettv)
2955{
2956 ufunc_T *fp;
2957 typval_T argvars[MAX_FUNC_ARGS + 1];
2958 int argcount = 0;
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002959 ocmember_T *ocm = NULL;
2960 int m_idx;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002961
2962 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
2963 if (fp == NULL)
2964 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002965 // could be an object or class funcref variable
2966 ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
2967 if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
2968 {
2969 method_not_found_msg(cl, rettv->v_type, name, len);
2970 return FAIL;
2971 }
2972
Yegappan Lakshmanan3e336502024-04-04 19:35:59 +02002973 if (*name == '_')
2974 {
2975 // Protected object or class funcref variable
2976 semsg(_(e_cannot_access_protected_variable_str), ocm->ocm_name,
2977 cl->class_name);
2978 return FAIL;
2979 }
2980
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002981 if (rettv->v_type == VAR_OBJECT)
2982 {
2983 // funcref object variable
2984 object_T *obj = rettv->vval.v_object;
2985 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2986 copy_tv(tv, rettv);
2987 }
2988 else
2989 // funcref class variable
2990 copy_tv(&cl->class_members_tv[m_idx], rettv);
2991 *arg = name_end;
2992 return OK;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002993 }
2994
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002995 if (ocm == NULL && *fp->uf_name == '_')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002996 {
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002997 // Cannot access a protected method outside of a class
Ernie Rael03042a22023-11-11 08:53:32 +01002998 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002999 return FAIL;
3000 }
3001
3002 char_u *argp = name_end;
Ernie Raelb077b582023-12-14 20:11:44 +01003003 int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount, FALSE);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003004 if (ret == FAIL)
3005 return FAIL;
3006
3007 funcexe_T funcexe;
3008 CLEAR_FIELD(funcexe);
3009 funcexe.fe_evaluate = TRUE;
3010 if (rettv->v_type == VAR_OBJECT)
3011 {
3012 funcexe.fe_object = rettv->vval.v_object;
3013 ++funcexe.fe_object->obj_refcount;
3014 }
3015
3016 // Clear the class or object after calling the function, in
3017 // case the refcount is one.
3018 typval_T tv_tofree = *rettv;
3019 rettv->v_type = VAR_UNKNOWN;
3020
3021 // Call the user function. Result goes into rettv;
3022 int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
3023 NULL);
3024
3025 // Clear the previous rettv and the arguments.
3026 clear_tv(&tv_tofree);
3027 for (int idx = 0; idx < argcount; ++idx)
3028 clear_tv(&argvars[idx]);
3029
3030 if (error != FCERR_NONE)
3031 {
3032 user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
3033 return FAIL;
3034 }
3035 *arg = argp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003036
3037 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003038}
3039
3040/*
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003041 * Create a partial typval for "obj.obj_method" and store it in "rettv".
3042 * Returns OK on success and FAIL on memory allocation failure.
3043 */
3044 int
3045obj_method_to_partial_tv(object_T *obj, ufunc_T *obj_method, typval_T *rettv)
3046{
3047 partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
3048 if (pt == NULL)
3049 return FAIL;
3050
3051 pt->pt_refcount = 1;
3052 if (obj != NULL)
3053 {
3054 pt->pt_obj = obj;
3055 ++pt->pt_obj->obj_refcount;
3056 }
3057 pt->pt_auto = TRUE;
3058 pt->pt_func = obj_method;
3059 func_ptr_ref(pt->pt_func);
3060
3061 rettv->v_type = VAR_PARTIAL;
3062 rettv->vval.v_partial = pt;
3063
3064 return OK;
3065}
3066
3067/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003068 * Evaluate what comes after a class:
3069 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003070 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003071 * - class constructor: SomeClass.new()
3072 * - object member: someObject.varname
3073 * - object method: someObject.SomeMethod()
3074 *
3075 * "*arg" points to the '.'.
3076 * "*arg" is advanced to after the member name or method call.
3077 *
3078 * Returns FAIL or OK.
3079 */
3080 int
3081class_object_index(
3082 char_u **arg,
3083 typval_T *rettv,
3084 evalarg_T *evalarg,
3085 int verbose UNUSED) // give error messages
3086{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003087 if (VIM_ISWHITE((*arg)[1]))
3088 {
3089 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
3090 return FAIL;
3091 }
3092
3093 ++*arg;
3094 char_u *name = *arg;
3095 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
3096 if (name_end == name)
3097 return FAIL;
3098 size_t len = name_end - name;
3099
Ernie Raeld615a312023-10-05 20:28:16 +02003100 int did_emsg_save = did_emsg;
Bram Moolenaar552bdca2023-02-17 21:08:50 +00003101 class_T *cl;
3102 if (rettv->v_type == VAR_CLASS)
3103 cl = rettv->vval.v_class;
3104 else // VAR_OBJECT
3105 {
3106 if (rettv->vval.v_object == NULL)
3107 {
3108 emsg(_(e_using_null_object));
3109 return FAIL;
3110 }
3111 cl = rettv->vval.v_object->obj_class;
3112 }
3113
Bram Moolenaard13dd302023-03-11 20:56:35 +00003114 if (cl == NULL)
3115 {
3116 emsg(_(e_incomplete_type));
3117 return FAIL;
3118 }
3119
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003120 if (*name_end == '(')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003121 // Invoke the class or object method
3122 return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003123
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003124 else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003125 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003126 // Search in the object member variable table and the class member
3127 // variable table.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003128 int is_object = rettv->v_type == VAR_OBJECT;
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003129 if (get_member_tv(cl, is_object, name, len, NULL, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003130 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003131 *arg = name_end;
3132 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003133 }
3134
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003135 // could be a class method or an object method
3136 int fidx;
3137 ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
3138 if (fp != NULL)
3139 {
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003140 // Protected methods are not accessible outside the class
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003141 if (*name == '_')
3142 {
Ernie Rael03042a22023-11-11 08:53:32 +01003143 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003144 return FAIL;
3145 }
3146
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003147 if (obj_method_to_partial_tv(is_object ? rettv->vval.v_object :
3148 NULL, fp, rettv) == FAIL)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003149 return FAIL;
3150
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003151 *arg = name_end;
3152 return OK;
3153 }
3154
Ernie Raeld615a312023-10-05 20:28:16 +02003155 if (did_emsg == did_emsg_save)
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +02003156 member_not_found_msg(cl, rettv->v_type, name, len);
Bram Moolenaard505d172022-12-18 21:42:55 +00003157 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003158
3159 return FAIL;
3160}
3161
3162/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003163 * If "arg" points to a class or object method, return it.
3164 * Otherwise return NULL.
3165 */
3166 ufunc_T *
3167find_class_func(char_u **arg)
3168{
3169 char_u *name = *arg;
3170 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
3171 if (name_end == name || *name_end != '.')
3172 return NULL;
3173
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003174 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003175 size_t len = name_end - name;
3176 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003177 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00003178 if (eval_variable(name, (int)len,
3179 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003180 return NULL;
3181 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00003182 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003183
3184 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
3185 : tv.vval.v_object->obj_class;
3186 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00003187 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003188 char_u *fname = name_end + 1;
3189 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
3190 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00003191 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003192 len = fname_end - fname;
3193
Ernie Rael4d00b832023-09-11 19:54:42 +02003194 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003195
Bram Moolenaareb533502022-12-14 15:06:11 +00003196fail_after_eval:
3197 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003198 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003199}
3200
3201/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003202 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003203 * Returns -1, if the variable is not found.
3204 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003205 */
3206 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003207class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003208{
Ernie Rael4d00b832023-09-11 19:54:42 +02003209 int idx;
3210 class_member_lookup(cl, name, namelen, &idx);
3211 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003212}
3213
3214/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003215 * Returns a pointer to the class member variable "name" in the class "cl".
3216 * Returns NULL if the variable is not found.
3217 * The member variable index is set in "idx".
3218 */
3219 ocmember_T *
3220class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3221{
Ernie Rael4d00b832023-09-11 19:54:42 +02003222 ocmember_T *ret_m = NULL;
3223 int ret_idx = -1;
3224 for (int i = 0; i < cl->class_class_member_count; ++i)
3225 {
3226 ocmember_T *m = &cl->class_class_members[i];
3227 if (namelen)
3228 {
3229 if (STRNCMP(name, m->ocm_name, namelen) == 0
3230 && m->ocm_name[namelen] == NUL)
3231 {
3232 ret_m = m;
3233 ret_idx = i;
3234 break;
3235 }
3236 }
3237 else if (STRCMP(name, m->ocm_name) == 0)
3238 {
3239 ret_m = m;
3240 ret_idx = i;
zeertzjqd9be94c2024-07-14 10:20:20 +02003241 break;
Ernie Rael4d00b832023-09-11 19:54:42 +02003242 }
3243 }
3244 if (idx != NULL)
3245 *idx = ret_idx;
3246 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003247}
3248
3249/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003250 * Returns a pointer to the class method "name" in class "cl".
3251 * Returns NULL if the method is not found.
3252 * The method index is set in "idx".
3253 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003254 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003255class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3256{
Ernie Rael4d00b832023-09-11 19:54:42 +02003257 ufunc_T *ret_fp = NULL;
3258 int ret_idx = -1;
3259 for (int i = 0; i < cl->class_class_function_count; ++i)
3260 {
3261 ufunc_T *fp = cl->class_class_functions[i];
3262 char_u *ufname = (char_u *)fp->uf_name;
3263 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
3264 {
3265 ret_fp = fp;
3266 ret_idx = i;
3267 break;
3268 }
3269 }
3270 if (idx != NULL)
3271 *idx = ret_idx;
3272 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003273}
3274
3275/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003276 * Returns the index of class method "name" in the class "cl".
3277 * Returns -1, if the method is not found.
3278 */
3279 int
3280class_method_idx(class_T *cl, char_u *name, size_t namelen)
3281{
3282 int idx;
3283 class_method_lookup(cl, name, namelen, &idx);
3284 return idx;
3285}
3286
3287/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003288 * Returns the index of object member variable "name" in the class "cl".
3289 * Returns -1, if the variable is not found.
3290 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
3291 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003292 static int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003293object_member_idx(class_T *cl, char_u *name, size_t namelen)
3294{
Ernie Rael4d00b832023-09-11 19:54:42 +02003295 int idx;
3296 object_member_lookup(cl, name, namelen, &idx);
3297 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02003298}
3299
3300/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003301 * Returns a pointer to the object member variable "name" in the class "cl".
3302 * Returns NULL if the variable is not found.
3303 * The object member variable index is set in "idx".
3304 */
3305 ocmember_T *
3306object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3307{
Ernie Rael4d00b832023-09-11 19:54:42 +02003308 ocmember_T *ret_m = NULL;
3309 int ret_idx = -1;
3310 for (int i = 0; i < cl->class_obj_member_count; ++i)
3311 {
3312 ocmember_T *m = &cl->class_obj_members[i];
3313 if (namelen)
3314 {
3315 if (STRNCMP(name, m->ocm_name, namelen) == 0
3316 && m->ocm_name[namelen] == NUL)
3317 {
3318 ret_m = m;
3319 ret_idx = i;
3320 break;
3321 }
3322 }
3323 else if (STRCMP(name, m->ocm_name) == 0)
zeertzjqd9be94c2024-07-14 10:20:20 +02003324 {
Ernie Rael4d00b832023-09-11 19:54:42 +02003325 ret_m = m;
3326 ret_idx = i;
zeertzjqd9be94c2024-07-14 10:20:20 +02003327 break;
3328 }
Ernie Rael4d00b832023-09-11 19:54:42 +02003329 }
3330 if (idx != NULL)
3331 *idx = ret_idx;
3332 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003333}
3334
3335/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003336 * Returns a pointer to the object method "name" in class "cl".
3337 * Returns NULL if the method is not found.
3338 * The object method index is set in "idx".
3339 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003340 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003341object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3342{
Ernie Rael4d00b832023-09-11 19:54:42 +02003343 ufunc_T *ret_fp = NULL;
3344 int ret_idx = -1;
3345 for (int i = 0; i < cl->class_obj_method_count; ++i)
3346 {
3347 ufunc_T *fp = cl->class_obj_methods[i];
3348 // Use a separate pointer to avoid that ASAN complains about
3349 // uf_name[] only being 4 characters.
3350 char_u *ufname = (char_u *)fp->uf_name;
3351 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
3352 {
3353 ret_fp = fp;
3354 ret_idx = i;
3355 break;
3356 }
3357 }
3358 if (idx != NULL)
3359 *idx = ret_idx;
3360 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003361}
3362
3363/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003364 * Returns the index of object method "name" in the class "cl".
3365 * Returns -1, if the method is not found.
3366 */
3367 int
3368object_method_idx(class_T *cl, char_u *name, size_t namelen)
3369{
3370 int idx;
3371 object_method_lookup(cl, name, namelen, &idx);
3372 return idx;
3373}
3374
3375/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003376 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
3377 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
3378 * object member variable.
3379 *
3380 * Returns a pointer to the member variable structure if variable is found.
3381 * Otherwise returns NULL. The member variable index is set in "*idx".
3382 */
3383 ocmember_T *
3384member_lookup(
3385 class_T *cl,
3386 vartype_T v_type,
3387 char_u *name,
3388 size_t namelen,
3389 int *idx)
3390{
3391 if (v_type == VAR_CLASS)
3392 return class_member_lookup(cl, name, namelen, idx);
3393 else
3394 return object_member_lookup(cl, name, namelen, idx);
3395}
3396
3397/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003398 * Find the class that defines the named member. Look up the hierarchy
3399 * starting at "cl".
3400 *
3401 * Return the class that defines the member "name", else NULL.
3402 * Fill in "p_m", if specified, for ocmember_T in found class.
3403 */
3404// NOTE: if useful for something could also indirectly return vartype and idx.
3405 static class_T *
3406class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m)
3407{
3408 class_T *cl_found = NULL;
3409 vartype_T vartype = VAR_UNKNOWN;
3410 ocmember_T *m_found = NULL;
3411
3412 len = len != 0 ? len : STRLEN(name);
3413
3414 // Loop assumes if member is not defined in "cl", then it is not
3415 // defined in any super class; the last class where it's found is the
3416 // class where it is defined. Once the vartype is found, the other
3417 // type is no longer checked.
3418 for (class_T *super = cl; super != NULL; super = super->class_extends)
3419 {
3420 class_T *cl_tmp = NULL;
3421 ocmember_T *m = NULL;
3422 if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT)
3423 {
3424 if ((m = object_member_lookup(super, name, len, NULL)) != NULL)
3425 {
3426 cl_tmp = super;
3427 vartype = VAR_OBJECT;
3428 }
3429 }
3430 if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS)
3431 {
3432 if (( m = class_member_lookup(super, name, len, NULL)) != NULL)
3433 {
3434 cl_tmp = super;
Zdenek Dohnal215c82d2024-12-04 20:19:40 +01003435 vartype = VAR_CLASS;
Ernie Raele6c9aa52023-10-06 19:55:52 +02003436 }
3437 }
3438 if (cl_tmp == NULL)
3439 break; // member is not in this or any super class.
3440 cl_found = cl_tmp;
3441 m_found = m;
3442 }
3443 if (p_m != NULL)
3444 *p_m = m_found;
3445 return cl_found;
3446}
3447
3448/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003449 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
3450 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
3451 *
3452 * Returns a pointer to the method structure if variable is found.
3453 * Otherwise returns NULL. The method variable index is set in "*idx".
3454 */
3455 ufunc_T *
3456method_lookup(
3457 class_T *cl,
3458 vartype_T v_type,
3459 char_u *name,
3460 size_t namelen,
3461 int *idx)
3462{
3463 if (v_type == VAR_CLASS)
3464 return class_method_lookup(cl, name, namelen, idx);
3465 else
3466 return object_method_lookup(cl, name, namelen, idx);
3467}
3468
3469/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00003470 * Return TRUE if current context "cctx_arg" is inside class "cl".
3471 * Return FALSE if not.
3472 */
3473 int
3474inside_class(cctx_T *cctx_arg, class_T *cl)
3475{
3476 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02003477 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003478 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00003479 return TRUE;
3480 return FALSE;
3481}
3482
3483/*
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01003484 * Return TRUE if object/class variable "m" is read-only.
3485 * Also give an error message.
3486 */
3487 int
3488oc_var_check_ro(class_T *cl, ocmember_T *m)
3489{
3490 if (m->ocm_flags & (OCMFLAG_FINAL | OCMFLAG_CONST))
3491 {
3492 semsg(_(e_cannot_change_readonly_variable_str_in_class_str),
3493 m->ocm_name, cl->class_name);
3494 return TRUE;
3495 }
3496 return FALSE;
3497}
3498
3499/*
3500 * Lock all the constant object variables. Called after creating and
3501 * initializing a new object.
3502 */
3503 void
3504obj_lock_const_vars(object_T *obj)
3505{
3506 for (int i = 0; i < obj->obj_class->class_obj_member_count; i++)
3507 {
3508 ocmember_T *ocm = &obj->obj_class->class_obj_members[i];
3509 if (ocm->ocm_flags & OCMFLAG_CONST)
3510 {
3511 typval_T *mtv = ((typval_T *)(obj + 1)) + i;
3512 item_lock(mtv, DICT_MAXNEST, TRUE, TRUE);
3513 }
3514 }
3515}
3516
3517/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003518 * Make a copy of an object.
3519 */
3520 void
3521copy_object(typval_T *from, typval_T *to)
3522{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003523 if (from->vval.v_object == NULL)
3524 to->vval.v_object = NULL;
3525 else
3526 {
3527 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003528 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003529 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003530}
3531
3532/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003533 * Make a copy of a class.
3534 */
3535 void
3536copy_class(typval_T *from, typval_T *to)
3537{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003538 if (from->vval.v_class == NULL)
3539 to->vval.v_class = NULL;
3540 else
3541 {
3542 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003543 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003544 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003545}
3546
3547/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003548 * Free the class "cl" and its contents.
3549 */
3550 static void
3551class_free(class_T *cl)
3552{
3553 // Freeing what the class contains may recursively come back here.
3554 // Clear "class_name" first, if it is NULL the class does not need to
3555 // be freed.
3556 VIM_CLEAR(cl->class_name);
3557
3558 class_unref(cl->class_extends);
3559
3560 for (int i = 0; i < cl->class_interface_count; ++i)
3561 {
3562 vim_free(((char_u **)cl->class_interfaces)[i]);
3563 if (cl->class_interfaces_cl[i] != NULL)
3564 class_unref(cl->class_interfaces_cl[i]);
3565 }
3566 vim_free(cl->class_interfaces);
3567 vim_free(cl->class_interfaces_cl);
3568
3569 itf2class_T *next;
3570 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
3571 {
3572 next = i2c->i2c_next;
3573 vim_free(i2c);
3574 }
3575
3576 for (int i = 0; i < cl->class_class_member_count; ++i)
3577 {
3578 ocmember_T *m = &cl->class_class_members[i];
3579 vim_free(m->ocm_name);
3580 vim_free(m->ocm_init);
3581 if (cl->class_members_tv != NULL)
3582 clear_tv(&cl->class_members_tv[i]);
3583 }
3584 vim_free(cl->class_class_members);
3585 vim_free(cl->class_members_tv);
3586
3587 for (int i = 0; i < cl->class_obj_member_count; ++i)
3588 {
3589 ocmember_T *m = &cl->class_obj_members[i];
3590 vim_free(m->ocm_name);
3591 vim_free(m->ocm_init);
3592 }
3593 vim_free(cl->class_obj_members);
3594
3595 for (int i = 0; i < cl->class_class_function_count; ++i)
3596 {
3597 ufunc_T *uf = cl->class_class_functions[i];
3598 func_clear_free(uf, FALSE);
3599 }
3600 vim_free(cl->class_class_functions);
3601
3602 for (int i = 0; i < cl->class_obj_method_count; ++i)
3603 {
3604 ufunc_T *uf = cl->class_obj_methods[i];
3605 func_clear_free(uf, FALSE);
3606 }
3607 vim_free(cl->class_obj_methods);
3608
3609 clear_type_list(&cl->class_type_list);
3610
3611 class_cleared(cl);
3612
3613 vim_free(cl);
3614}
3615
3616/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003617 * Unreference a class. Free it when the reference count goes down to zero.
3618 */
3619 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003620class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003621{
Bram Moolenaard505d172022-12-18 21:42:55 +00003622 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003623 class_free(cl);
3624}
3625
3626/*
3627 * Go through the list of all classes and free items without "copyID".
3628 */
3629 int
3630class_free_nonref(int copyID)
3631{
3632 int did_free = FALSE;
3633
3634 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003635 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003636 next_nonref_class = cl->class_next_used;
3637 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003638 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003639 // Free the class and items it contains.
3640 class_free(cl);
3641 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003642 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003643 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003644
3645 next_nonref_class = NULL;
3646 return did_free;
3647}
3648
3649 int
3650set_ref_in_classes(int copyID)
3651{
3652 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01003653 set_ref_in_item_class(cl, copyID, NULL, NULL, NULL);
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003654
3655 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003656}
3657
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003658static object_T *first_object = NULL;
3659
3660/*
3661 * Call this function when an object has been created. It will be added to the
3662 * list headed by "first_object".
3663 */
3664 void
3665object_created(object_T *obj)
3666{
3667 if (first_object != NULL)
3668 {
3669 obj->obj_next_used = first_object;
3670 first_object->obj_prev_used = obj;
3671 }
3672 first_object = obj;
3673}
3674
3675/*
3676 * Call this function when an object has been cleared and is about to be freed.
3677 * It is removed from the list headed by "first_object".
3678 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003679 static void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003680object_cleared(object_T *obj)
3681{
3682 if (obj->obj_next_used != NULL)
3683 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
3684 if (obj->obj_prev_used != NULL)
3685 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
3686 else if (first_object == obj)
3687 first_object = obj->obj_next_used;
3688}
3689
3690/*
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003691 * Free the contents of an object ignoring the reference count.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003692 */
3693 static void
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003694object_free_contents(object_T *obj)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003695{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003696 class_T *cl = obj->obj_class;
3697
3698 if (!cl)
3699 return;
3700
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003701 // Avoid a recursive call, it can happen if "obj" has a circular reference.
3702 obj->obj_refcount = INT_MAX;
3703
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003704 // the member values are just after the object structure
3705 typval_T *tv = (typval_T *)(obj + 1);
3706 for (int i = 0; i < cl->class_obj_member_count; ++i)
3707 clear_tv(tv + i);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003708}
3709
3710 static void
3711object_free_object(object_T *obj)
3712{
3713 class_T *cl = obj->obj_class;
3714
3715 if (!cl)
3716 return;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003717
3718 // Remove from the list headed by "first_object".
3719 object_cleared(obj);
3720
3721 vim_free(obj);
3722 class_unref(cl);
3723}
3724
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003725 static void
3726object_free(object_T *obj)
3727{
3728 if (in_free_unref_items)
3729 return;
3730
3731 object_free_contents(obj);
3732 object_free_object(obj);
3733}
3734
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003735/*
3736 * Unreference an object.
3737 */
3738 void
3739object_unref(object_T *obj)
3740{
3741 if (obj != NULL && --obj->obj_refcount <= 0)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003742 object_free(obj);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003743}
3744
3745/*
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003746 * Go through the list of all objects and free items without "copyID".
3747 */
3748 int
3749object_free_nonref(int copyID)
3750{
3751 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003752
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003753 for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003754 {
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003755 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3756 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003757 // Free the object contents. Object itself will be freed later.
3758 object_free_contents(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003759 did_free = TRUE;
3760 }
3761 }
3762
3763 return did_free;
3764}
3765
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003766 void
3767object_free_items(int copyID)
3768{
3769 object_T *obj_next;
3770
3771 for (object_T *obj = first_object; obj != NULL; obj = obj_next)
3772 {
3773 obj_next = obj->obj_next_used;
3774 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3775 object_free_object(obj);
3776 }
3777}
3778
LemonBoyafe04662023-08-23 21:08:11 +02003779/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003780 * Output message which takes a variable name and the class that defines it.
3781 * "cl" is that class where the name was found. Search "cl"'s hierarchy to
3782 * find the defining class.
3783 */
3784 void
3785emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl)
3786{
3787 ocmember_T *m;
3788 class_T *cl_def = class_defining_member(cl, name, len, &m);
3789 if (cl_def != NULL)
3790 semsg(_(msg), m->ocm_name, cl_def->class_name);
3791 else
3792 emsg(_(e_internal_error_please_report_a_bug));
3793}
3794
3795/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003796 * Echo a class or object method not found message.
3797 */
3798 void
3799method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3800{
3801 char_u *method_name = vim_strnsave(name, len);
3802 if ((v_type == VAR_OBJECT)
3803 && (class_method_idx(cl, name, len) >= 0))
3804 {
3805 // If this is a class method, then give a different error
3806 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003807 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003808 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003809 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003810 method_name, cl->class_name);
3811 }
3812 else if ((v_type == VAR_CLASS)
3813 && (object_method_idx(cl, name, len) >= 0))
3814 {
3815 // If this is an object method, then give a different error
3816 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003817 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003818 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003819 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003820 method_name, cl->class_name);
3821 }
3822 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003823 semsg(_(e_method_not_found_on_class_str_str), method_name,
zeertzjqd9be94c2024-07-14 10:20:20 +02003824 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003825 vim_free(method_name);
3826}
3827
3828/*
3829 * Echo a class or object member not found message.
3830 */
3831 void
3832member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3833{
3834 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
3835
3836 if (v_type == VAR_OBJECT)
3837 {
3838 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003839 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003840 varname, cl->class_name);
3841 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003842 semsg(_(e_variable_not_found_on_object_str_str), varname,
3843 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003844 }
3845 else
3846 {
3847 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003848 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003849 varname, cl->class_name);
3850 else
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01003851 {
3852 if (IS_ENUM(cl))
3853 semsg(_(e_enum_value_str_not_found_in_enum_str),
3854 varname, cl->class_name);
3855 else
3856 semsg(_(e_class_variable_str_not_found_in_class_str),
3857 varname, cl->class_name);
3858 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003859 }
3860 vim_free(varname);
3861}
3862
3863/*
Yegappan Lakshmanan4f32c832024-01-12 17:36:40 +01003864 * Compile all the class and object methods in "cl".
3865 */
3866 void
3867defcompile_class(class_T *cl)
3868{
3869 for (int loop = 1; loop <= 2; ++loop)
3870 {
3871 int func_count = loop == 1 ? cl->class_class_function_count
3872 : cl->class_obj_method_count;
3873 for (int i = 0; i < func_count; i++)
3874 {
3875 ufunc_T *ufunc = loop == 1 ? cl->class_class_functions[i]
3876 : cl->class_obj_methods[i];
Yegappan Lakshmanan1af0fbf2024-04-09 21:39:27 +02003877 // Don't compile abstract methods
3878 if (!IS_ABSTRACT_METHOD(ufunc))
3879 defcompile_function(ufunc, cl);
Yegappan Lakshmanan4f32c832024-01-12 17:36:40 +01003880 }
3881 }
3882}
3883
3884/*
3885 * Compile all the classes defined in the current script
3886 */
3887 void
3888defcompile_classes_in_script(void)
3889{
3890 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3891 {
3892 if (eval_variable(cl->class_name, 0, 0, NULL, NULL,
3893 EVAL_VAR_NOAUTOLOAD | EVAL_VAR_NO_FUNC) != FAIL)
3894 defcompile_class(cl);
3895 }
3896}
3897
3898/*
3899 * Returns TRUE if "name" is the name of a class. The typval for the class is
3900 * returned in "rettv".
3901 */
3902 int
3903is_class_name(char_u *name, typval_T *rettv)
3904{
3905 rettv->v_type = VAR_UNKNOWN;
3906
3907 if (eval_variable(name, 0, 0, rettv, NULL, EVAL_VAR_NOAUTOLOAD |
3908 EVAL_VAR_NO_FUNC) != FAIL)
3909 return rettv->v_type == VAR_CLASS;
3910 return FALSE;
3911}
3912
3913/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003914 * Calls the object builtin method "name" with arguments "argv". The value
3915 * returned by the builtin method is in "rettv". Returns OK or FAIL.
3916 */
3917 static int
3918object_call_builtin_method(
3919 object_T *obj,
3920 class_builtin_T builtin_method,
3921 int argc,
3922 typval_T *argv,
3923 typval_T *rettv)
3924{
3925 ufunc_T *uf;
3926 int midx;
3927
3928 if (obj == NULL)
3929 return FAIL;
3930
3931 uf = class_get_builtin_method(obj->obj_class, builtin_method, &midx);
3932 if (uf == NULL)
3933 return FAIL;
3934
3935 funccall_T *fc = create_funccal(uf, rettv);
3936 int r;
3937
3938 if (fc == NULL)
3939 return FAIL;
3940
3941 ++obj->obj_refcount;
3942
3943 r = call_def_function(uf, argc, argv, 0, NULL, obj, fc, rettv);
3944
3945 remove_funccal();
3946
3947 return r;
3948}
3949
3950/*
zeertzjqc029c132024-03-28 11:37:26 +01003951 * Calls the object "empty()" method and returns the method return value. In
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003952 * case of an error, returns TRUE.
3953 */
3954 int
3955object_empty(object_T *obj)
3956{
3957 typval_T rettv;
3958
3959 if (object_call_builtin_method(obj, CLASS_BUILTIN_EMPTY, 0, NULL, &rettv)
3960 == FAIL)
3961 return TRUE;
3962
3963 return tv_get_bool(&rettv);
3964}
3965
3966/*
3967 * Use the object "len()" method to get an object length. Returns 0 if the
3968 * method is not found or there is an error.
3969 */
3970 int
3971object_len(object_T *obj)
3972{
3973 typval_T rettv;
3974
3975 if (object_call_builtin_method(obj, CLASS_BUILTIN_LEN, 0, NULL, &rettv)
3976 == FAIL)
3977 return 0;
3978
3979 return tv_to_number(&rettv);
3980}
3981
3982/*
LemonBoy7b29cc92024-06-22 17:25:07 +02003983 * Return TRUE when two objects have exactly the same values.
3984 */
3985 int
3986object_equal(
3987 object_T *o1,
3988 object_T *o2,
Yinzuo Jiang7ccd1a22024-07-04 17:20:53 +02003989 int ic) // ignore case for strings
LemonBoy7b29cc92024-06-22 17:25:07 +02003990{
3991 class_T *cl1, *cl2;
3992
3993 if (o1 == o2)
3994 return TRUE;
Ernie Rael86257142024-06-23 09:54:45 +02003995 if (o1 == NULL || o2 == NULL)
3996 return FALSE;
LemonBoy7b29cc92024-06-22 17:25:07 +02003997
3998 cl1 = o1->obj_class;
3999 cl2 = o2->obj_class;
4000
4001 if (cl1 != cl2 || cl1 == NULL || cl2 == NULL)
4002 return FALSE;
4003
4004 for (int i = 0; i < cl1->class_obj_member_count; ++i)
Yinzuo Jiang7ccd1a22024-07-04 17:20:53 +02004005 if (!tv_equal((typval_T *)(o1 + 1) + i, (typval_T *)(o2 + 1) + i, ic))
LemonBoy7b29cc92024-06-22 17:25:07 +02004006 return FALSE;
4007
4008 return TRUE;
4009}
4010
4011/*
Ernie Rael05ff4e42024-07-04 16:50:11 +02004012 * Return a textual representation of object "obj".
4013 * "obj" must not be NULL.
4014 * May return NULL.
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004015 */
4016 char_u *
Yegappan Lakshmanan22029ed2024-05-20 13:57:11 +02004017object2string(
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004018 object_T *obj,
4019 char_u *numbuf,
4020 int copyID,
4021 int echo_style,
4022 int restore_copyID,
4023 int composite_val)
4024{
4025 typval_T rettv;
4026
4027 if (object_call_builtin_method(obj, CLASS_BUILTIN_STRING, 0, NULL, &rettv)
4028 == OK
4029 && rettv.vval.v_string != NULL)
4030 return rettv.vval.v_string;
Ernie Rael05ff4e42024-07-04 16:50:11 +02004031
4032 int ok = OK;
4033 class_T *cl = obj->obj_class;
4034 garray_T ga;
4035 ga_init2(&ga, 1, 50);
4036
4037 if (cl != NULL && IS_ENUM(cl))
4038 {
4039 ga_concat(&ga, (char_u *)"enum ");
4040 ga_concat(&ga, cl->class_name);
4041 char_u *enum_name = ((typval_T *)(obj + 1))->vval.v_string;
4042 ga_concat(&ga, (char_u *)".");
4043 ga_concat(&ga, enum_name);
4044 }
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004045 else
4046 {
Ernie Rael05ff4e42024-07-04 16:50:11 +02004047 ga_concat(&ga, (char_u *)"object of ");
4048 ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]"
4049 : cl->class_name);
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004050 }
Ernie Rael05ff4e42024-07-04 16:50:11 +02004051 if (cl != NULL)
4052 {
4053 ga_concat(&ga, (char_u *)" {");
4054 for (int i = 0; i < cl->class_obj_member_count; ++i)
4055 {
4056 if (i > 0)
4057 ga_concat(&ga, (char_u *)", ");
4058 ocmember_T *m = &cl->class_obj_members[i];
4059 ga_concat(&ga, m->ocm_name);
4060 ga_concat(&ga, (char_u *)": ");
4061 char_u *tf = NULL;
4062 char_u *s = echo_string_core((typval_T *)(obj + 1) + i,
4063 &tf, numbuf, copyID, echo_style,
4064 restore_copyID, composite_val);
4065 if (s != NULL)
4066 ga_concat(&ga, s);
4067 vim_free(tf);
4068 if (s == NULL || did_echo_string_emsg)
4069 {
4070 ok = FAIL;
4071 break;
4072 }
4073 line_breakcheck();
4074 }
4075 ga_concat(&ga, (char_u *)"}");
4076 }
4077 if (ok == FAIL)
4078 {
4079 vim_free(ga.ga_data);
4080 return NULL;
4081 }
Yegappan Lakshmanancb848b62025-01-20 21:38:09 +01004082 ga_append(&ga, NUL);
Ernie Rael05ff4e42024-07-04 16:50:11 +02004083 return (char_u *)ga.ga_data;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004084}
4085
4086/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02004087 * Return TRUE when the class "cl", its base class or one of the implemented
4088 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02004089 */
4090 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004091class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02004092{
4093 if (cl == other_cl)
4094 return TRUE;
4095
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004096 // Recursively check the base classes.
4097 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02004098 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004099 if (cl == other_cl)
4100 return TRUE;
4101 // Check the implemented interfaces and the super interfaces
4102 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02004103 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004104 class_T *intf = cl->class_interfaces_cl[i];
4105 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02004106 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004107 if (intf == other_cl)
4108 return TRUE;
4109 // check the super interfaces
4110 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02004111 }
4112 }
LemonBoyafe04662023-08-23 21:08:11 +02004113 }
4114
4115 return FALSE;
4116}
4117
4118/*
Ernie Rael2025af12023-12-12 16:58:00 +01004119 * "instanceof(object, classinfo, ...)" function
LemonBoyafe04662023-08-23 21:08:11 +02004120 */
4121 void
4122f_instanceof(typval_T *argvars, typval_T *rettv)
4123{
4124 typval_T *object_tv = &argvars[0];
4125 typval_T *classinfo_tv = &argvars[1];
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02004126 class_T *c;
LemonBoyafe04662023-08-23 21:08:11 +02004127
4128 rettv->vval.v_number = VVAL_FALSE;
4129
4130 if (check_for_object_arg(argvars, 0) == FAIL
Ernie Rael2025af12023-12-12 16:58:00 +01004131 || check_for_class_or_typealias_args(argvars, 1) == FAIL)
LemonBoyafe04662023-08-23 21:08:11 +02004132 return;
4133
Ernie Rael3da696d2023-09-19 20:14:18 +02004134 if (object_tv->vval.v_object == NULL)
Yegappan Lakshmanan6fa62082025-04-03 21:26:34 +02004135 {
4136 if (classinfo_tv->vval.v_class == NULL)
4137 // consider null_object as an instance of null_class
4138 rettv->vval.v_number = VVAL_TRUE;
Ernie Rael3da696d2023-09-19 20:14:18 +02004139 return;
Yegappan Lakshmanan6fa62082025-04-03 21:26:34 +02004140 }
Ernie Rael3da696d2023-09-19 20:14:18 +02004141
Ernie Rael2025af12023-12-12 16:58:00 +01004142 for (; classinfo_tv->v_type != VAR_UNKNOWN; ++classinfo_tv)
LemonBoyafe04662023-08-23 21:08:11 +02004143 {
Ernie Rael2025af12023-12-12 16:58:00 +01004144 if (classinfo_tv->v_type == VAR_TYPEALIAS)
4145 c = classinfo_tv->vval.v_typealias->ta_type->tt_class;
4146 else
4147 c = classinfo_tv->vval.v_class;
4148
4149 if (class_instance_of(object_tv->vval.v_object->obj_class, c))
LemonBoyafe04662023-08-23 21:08:11 +02004150 {
Ernie Rael2025af12023-12-12 16:58:00 +01004151 rettv->vval.v_number = VVAL_TRUE;
4152 return;
LemonBoyafe04662023-08-23 21:08:11 +02004153 }
4154 }
LemonBoyafe04662023-08-23 21:08:11 +02004155}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00004156
4157#endif // FEAT_EVAL