blob: dc13c4b2e965270e1e042b87b28b9da9cfd09799 [file] [log] [blame]
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * vim9class.c: Vim9 script class support
12 */
13
14#define USING_FLOAT_STUFF
15#include "vim.h"
16
17#if defined(FEAT_EVAL) || defined(PROTO)
18
19// When not generating protos this is included in proto.h
20#ifdef PROTO
21# include "vim9.h"
22#endif
23
Yegappan Lakshmanane651e112023-09-04 07:51:01 +020024static class_T *first_class = NULL;
25static class_T *next_nonref_class = NULL;
26
27/*
28 * Call this function when a class has been created. It will be added to the
29 * list headed by "first_class".
30 */
31 static void
32class_created(class_T *cl)
33{
34 if (first_class != NULL)
35 {
36 cl->class_next_used = first_class;
37 first_class->class_prev_used = cl;
38 }
39 first_class = cl;
40}
41
42/*
43 * Call this function when a class has been cleared and is about to be freed.
44 * It is removed from the list headed by "first_class".
45 */
46 static void
47class_cleared(class_T *cl)
48{
49 if (cl->class_next_used != NULL)
50 cl->class_next_used->class_prev_used = cl->class_prev_used;
51 if (cl->class_prev_used != NULL)
52 cl->class_prev_used->class_next_used = cl->class_next_used;
53 else if (first_class == cl)
54 first_class = cl->class_next_used;
55
56 // update the next class to check if needed
57 if (cl == next_nonref_class)
58 next_nonref_class = cl->class_next_used;
59}
60
Bram Moolenaarc1c365c2022-12-04 20:13:24 +000061/*
Bram Moolenaard505d172022-12-18 21:42:55 +000062 * Parse a member declaration, both object and class member.
63 * Returns OK or FAIL. When OK then "varname_end" is set to just after the
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +020064 * variable name and "type_ret" is set to the declared or detected type.
Bram Moolenaard505d172022-12-18 21:42:55 +000065 * "init_expr" is set to the initialisation expression (allocated), if there is
Bram Moolenaar554d0312023-01-05 19:59:18 +000066 * one. For an interface "init_expr" is NULL.
Bram Moolenaard505d172022-12-18 21:42:55 +000067 */
68 static int
69parse_member(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020070 exarg_T *eap,
71 char_u *line,
72 char_u *varname,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020073 int has_public, // TRUE if "public" seen before "varname"
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020074 char_u **varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +020075 int *has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020076 garray_T *type_list,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020077 type_T **type_ret,
78 char_u **init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +000079{
80 *varname_end = to_name_end(varname, FALSE);
81 if (*varname == '_' && has_public)
82 {
RestorerZ7fe8f432023-09-24 23:21:24 +020083 semsg(_(e_public_variable_name_cannot_start_with_underscore_str), line);
Bram Moolenaard505d172022-12-18 21:42:55 +000084 return FAIL;
85 }
86
87 char_u *colon = skipwhite(*varname_end);
88 char_u *type_arg = colon;
89 type_T *type = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +020090 *has_type = FALSE;
Bram Moolenaard505d172022-12-18 21:42:55 +000091 if (*colon == ':')
92 {
93 if (VIM_ISWHITE(**varname_end))
94 {
95 semsg(_(e_no_white_space_allowed_before_colon_str), varname);
96 return FAIL;
97 }
98 if (!VIM_ISWHITE(colon[1]))
99 {
100 semsg(_(e_white_space_required_after_str_str), ":", varname);
101 return FAIL;
102 }
103 type_arg = skipwhite(colon + 1);
104 type = parse_type(&type_arg, type_list, TRUE);
105 if (type == NULL)
106 return FAIL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200107 *has_type = TRUE;
Bram Moolenaard505d172022-12-18 21:42:55 +0000108 }
109
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200110 char_u *init_arg = skipwhite(type_arg);
111 if (type == NULL && *init_arg != '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000112 {
113 emsg(_(e_type_or_initialization_required));
114 return FAIL;
115 }
116
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200117 if (init_expr == NULL && *init_arg == '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000118 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200119 emsg(_(e_cannot_initialize_variable_in_interface));
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200120 return FAIL;
121 }
122
123 if (*init_arg == '=')
124 {
125 evalarg_T evalarg;
126 char_u *expr_start, *expr_end;
127
128 if (!VIM_ISWHITE(init_arg[-1]) || !VIM_ISWHITE(init_arg[1]))
Bram Moolenaard505d172022-12-18 21:42:55 +0000129 {
130 semsg(_(e_white_space_required_before_and_after_str_at_str),
131 "=", type_arg);
132 return FAIL;
133 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200134 init_arg = skipwhite(init_arg + 1);
Bram Moolenaard505d172022-12-18 21:42:55 +0000135
Bram Moolenaard505d172022-12-18 21:42:55 +0000136 fill_evalarg_from_eap(&evalarg, eap, FALSE);
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200137 (void)skip_expr_concatenate(&init_arg, &expr_start, &expr_end, &evalarg);
Bram Moolenaard505d172022-12-18 21:42:55 +0000138
Yegappan Lakshmananac773182024-04-27 11:36:12 +0200139 init_arg = skipwhite(init_arg);
Yegappan Lakshmananfe55c312024-04-28 09:54:09 +0200140 if (*init_arg != NUL && !vim9_comment_start(init_arg))
Yegappan Lakshmananac773182024-04-27 11:36:12 +0200141 {
142 semsg(_(e_trailing_characters_str), init_arg);
143 return FAIL;
144 }
145
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +0200146 // No type specified for the member. Set it to "any" and the correct
147 // type will be set when the object is instantiated.
Bram Moolenaard505d172022-12-18 21:42:55 +0000148 if (type == NULL)
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200149 type = &t_any;
Bram Moolenaard505d172022-12-18 21:42:55 +0000150
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200151 *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
152 // Free the memory pointed by expr_start.
Bram Moolenaard505d172022-12-18 21:42:55 +0000153 clear_evalarg(&evalarg, NULL);
154 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200155 else if (!valid_declaration_type(type))
Bram Moolenaard505d172022-12-18 21:42:55 +0000156 return FAIL;
157
158 *type_ret = type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000159 return OK;
160}
161
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100162typedef struct oc_newmember_S oc_newmember_T;
163struct oc_newmember_S
164{
165 garray_T *gap;
166 char_u *varname;
167 char_u *varname_end;
168 int has_public;
169 int has_final;
170 int has_type;
171 type_T *type;
172 char_u *init_expr;
173};
174
Bram Moolenaard505d172022-12-18 21:42:55 +0000175/*
176 * Add a member to an object or a class.
177 * Returns OK when successful, "init_expr" will be consumed then.
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100178 * Returns OK on success and FAIL on memory allocation failure (caller might
179 * need to free "init_expr").
Bram Moolenaard505d172022-12-18 21:42:55 +0000180 */
181 static int
182add_member(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200183 garray_T *gap,
184 char_u *varname,
185 char_u *varname_end,
186 int has_public,
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100187 int has_final,
188 int has_const,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200189 int has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200190 type_T *type,
191 char_u *init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +0000192{
193 if (ga_grow(gap, 1) == FAIL)
194 return FAIL;
195 ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
196 m->ocm_name = vim_strnsave(varname, varname_end - varname);
=?UTF-8?q?Ola=20S=C3=B6der?=d8742472023-03-05 13:12:32 +0000197 m->ocm_access = has_public ? VIM_ACCESS_ALL
198 : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100199 if (has_final)
200 m->ocm_flags |= OCMFLAG_FINAL;
201 if (has_const)
202 m->ocm_flags |= OCMFLAG_CONST;
203 if (has_type)
204 m->ocm_flags |= OCMFLAG_HAS_TYPE;
Bram Moolenaard505d172022-12-18 21:42:55 +0000205 m->ocm_type = type;
206 if (init_expr != NULL)
207 m->ocm_init = init_expr;
208 ++gap->ga_len;
209 return OK;
210}
211
212/*
213 * Move the class or object members found while parsing a class into the class.
214 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000215 * "parent_members" points to the members in the parent class (if any)
216 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000217 * "members" will be set to the newly allocated array of members and
218 * "member_count" set to the number of members.
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100219 * Returns OK on success and FAIL on memory allocation failure.
Bram Moolenaard505d172022-12-18 21:42:55 +0000220 */
221 static int
222add_members_to_class(
223 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000224 ocmember_T *parent_members,
225 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000226 ocmember_T **members,
227 int *member_count)
228{
Bram Moolenaar83677162023-01-08 19:54:10 +0000229 *member_count = parent_count + gap->ga_len;
230 *members = *member_count == 0 ? NULL
231 : ALLOC_MULT(ocmember_T, *member_count);
232 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000233 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000234 for (int i = 0; i < parent_count; ++i)
235 {
236 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000237 ocmember_T *m = *members + i;
238 *m = parent_members[i];
239 m->ocm_name = vim_strsave(m->ocm_name);
240 if (m->ocm_init != NULL)
241 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000242 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000243 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000244 // new members are moved
245 mch_memmove(*members + parent_count,
246 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000247 VIM_CLEAR(gap->ga_data);
248 return OK;
249}
250
251/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000252 * Convert a member index "idx" of interface "itf" to the member index of class
253 * "cl" implementing that interface.
254 */
255 int
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200256object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000257{
Ernie Rael18143d32023-09-04 22:30:41 +0200258 if (idx >= (is_method ? itf->class_obj_method_count
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200259 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000260 {
261 siemsg("index %d out of range for interface %s", idx, itf->class_name);
262 return 0;
263 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200264
265 // If "cl" is the interface or the class that is extended, then the method
266 // index can be used directly and there is no need to search for the method
267 // index in one of the child classes.
268 if (cl == itf)
269 return idx;
270
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200271 itf2class_T *i2c = NULL;
272 int searching = TRUE;
273 int method_offset = 0;
274
Ernie Raelcf138d42023-09-06 20:45:03 +0200275 for (class_T *super = cl; super != NULL && searching;
276 super = super->class_extends)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200277 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200278 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200279 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200280 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
281 {
282 searching = FALSE;
283 break;
284 }
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200285 }
286 if (searching && is_method)
287 // The parent class methods are stored after the current class
288 // methods.
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200289 method_offset += super->class_obj_method_count_child;
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200290 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000291 if (i2c == NULL)
292 {
293 siemsg("class %s not found on interface %s",
294 cl->class_name, itf->class_name);
295 return 0;
296 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +0200297
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200298 // A table follows the i2c for the class
299 int *table = (int *)(i2c + 1);
300 // "method_offset" is 0, if method is in the current class. If method
301 // is in a parent class, then it is non-zero.
302 return table[idx] + method_offset;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000303}
304
305/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200306 * Check whether a class named "extends_name" is present. If the class is
307 * valid, then "extends_clp" is set with the class pointer.
308 * Returns TRUE if the class name "extends_names" is a valid class.
309 */
310 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200311validate_extends_class(
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100312 class_T *cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200313 char_u *extends_name,
314 class_T **extends_clp,
315 int is_class)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200316{
317 typval_T tv;
318 int success = FALSE;
319
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100320 if (STRCMP(cl->class_name, extends_name) == 0)
321 {
322 semsg(_(e_cannot_extend_str), extends_name);
323 return success;
324 }
325
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200326 tv.v_type = VAR_UNKNOWN;
327 if (eval_variable_import(extends_name, &tv) == FAIL)
328 {
329 semsg(_(e_class_name_not_found_str), extends_name);
330 return success;
331 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200332
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200333 if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100334 || (is_class && IS_INTERFACE(tv.vval.v_class))
335 || (!is_class && !IS_INTERFACE(tv.vval.v_class))
336 || (is_class && IS_ENUM(tv.vval.v_class)))
337 {
338 // a class cannot extend an interface
339 // an interface cannot extend a class
340 // a class cannot extend an enum.
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200341 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100342 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200343 else
344 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200345 class_T *extends_cl = tv.vval.v_class;
346 ++extends_cl->class_refcount;
347 *extends_clp = extends_cl;
348 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200349 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200350 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200351
352 return success;
353}
354
355/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200356 * Check method names in the parent class lineage to make sure the access is
357 * the same for overridden methods.
358 */
359 static int
360validate_extends_methods(
361 garray_T *objmethods_gap,
362 class_T *extends_cl)
363{
364 class_T *super = extends_cl;
365 int method_count = objmethods_gap->ga_len;
366 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
367
368 while (super != NULL)
369 {
370 int extends_method_count = super->class_obj_method_count_child;
371 if (extends_method_count == 0)
372 {
373 super = super->class_extends;
374 continue;
375 }
376
377 ufunc_T **extends_methods = super->class_obj_methods;
378
379 for (int i = 0; i < extends_method_count; i++)
380 {
381 char_u *pstr = extends_methods[i]->uf_name;
382 int extends_private = (*pstr == '_');
383 if (extends_private)
384 pstr++;
385
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200386 // When comparing the method names, ignore the access type (public
387 // and private methods are considered the same).
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200388 for (int j = 0; j < method_count; j++)
389 {
390 char_u *qstr = cl_fp[j]->uf_name;
391 int priv_method = (*qstr == '_');
392 if (priv_method)
393 qstr++;
394 if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
395 {
396 // Method access is different between the super class and
397 // the subclass
398 semsg(_(e_method_str_of_class_str_has_different_access),
399 cl_fp[j]->uf_name, super->class_name);
400 return FALSE;
401 }
402 }
403 }
404 super = super->class_extends;
405 }
406
407 return TRUE;
408}
409
410/*
411 * Check whether a object member variable in "objmembers_gap" is a duplicate of
412 * a member in any of the extended parent class lineage. Returns TRUE if there
413 * are no duplicates.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200414 */
415 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200416extends_check_dup_members(
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200417 garray_T *objmembers_gap,
418 class_T *extends_cl)
419{
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200420 int member_count = objmembers_gap->ga_len;
421 if (member_count == 0)
422 return TRUE;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200423
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200424 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
425
426 // Validate each member variable
427 for (int c_i = 0; c_i < member_count; c_i++)
428 {
429 class_T *p_cl = extends_cl;
430 ocmember_T *c_m = members + c_i;
431 char_u *pstr = (*c_m->ocm_name == '_')
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200432 ? c_m->ocm_name + 1 : c_m->ocm_name;
433
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200434 // Check in all the parent classes in the lineage
435 while (p_cl != NULL)
436 {
437 int p_member_count = p_cl->class_obj_member_count;
438 if (p_member_count == 0)
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200439 {
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200440 p_cl = p_cl->class_extends;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200441 continue;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200442 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200443 ocmember_T *p_members = p_cl->class_obj_members;
444
445 // Compare against all the members in the parent class
446 for (int p_i = 0; p_i < p_member_count; p_i++)
447 {
448 ocmember_T *p_m = p_members + p_i;
449 char_u *qstr = (*p_m->ocm_name == '_')
450 ? p_m->ocm_name + 1 : p_m->ocm_name;
451 if (STRCMP(pstr, qstr) == 0)
452 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200453 semsg(_(e_duplicate_variable_str), c_m->ocm_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200454 return FALSE;
455 }
456 }
457
458 p_cl = p_cl->class_extends;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200459 }
460 }
461
462 return TRUE;
463}
464
465/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200466 * Compare the variable type of interface variables in "objmembers_gap" against
467 * the variable in any of the extended super interface lineage. Used to
468 * compare the variable types when extending interfaces. Returns TRUE if the
469 * variable types are the same.
470 */
471 static int
472extends_check_intf_var_type(
473 garray_T *objmembers_gap,
474 class_T *extends_cl)
475{
476 int member_count = objmembers_gap->ga_len;
477 if (member_count == 0)
478 return TRUE;
479
480 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
481
482 // Validate each member variable
483 for (int c_i = 0; c_i < member_count; c_i++)
484 {
485 class_T *p_cl = extends_cl;
486 ocmember_T *c_m = members + c_i;
487 int var_found = FALSE;
488
489 // Check in all the parent classes in the lineage
490 while (p_cl != NULL && !var_found)
491 {
492 int p_member_count = p_cl->class_obj_member_count;
493 if (p_member_count == 0)
494 {
495 p_cl = p_cl->class_extends;
496 continue;
497 }
498 ocmember_T *p_members = p_cl->class_obj_members;
499
500 // Compare against all the members in the parent class
501 for (int p_i = 0; p_i < p_member_count; p_i++)
502 {
503 where_T where = WHERE_INIT;
504 ocmember_T *p_m = p_members + p_i;
505
506 if (STRCMP(p_m->ocm_name, c_m->ocm_name) != 0)
507 continue;
508
509 // Ensure the type is matching.
510 where.wt_func_name = (char *)c_m->ocm_name;
511 where.wt_kind = WT_MEMBER;
512
513 if (check_type(p_m->ocm_type, c_m->ocm_type, TRUE,
514 where) == FAIL)
515 return FALSE;
516
517 var_found = TRUE;
518 }
519
520 p_cl = p_cl->class_extends;
521 }
522 }
523
524 return TRUE;
525}
526
527/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200528 * When extending an abstract class, check whether all the abstract methods in
529 * the parent class are implemented. Returns TRUE if all the methods are
530 * implemented.
531 */
532 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200533validate_abstract_class_methods(
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200534 garray_T *classmethods_gap,
535 garray_T *objmethods_gap,
536 class_T *extends_cl)
537{
538 for (int loop = 1; loop <= 2; ++loop)
539 {
540 // loop == 1: check class methods
541 // loop == 2: check object methods
542 int extends_method_count = loop == 1
543 ? extends_cl->class_class_function_count
544 : extends_cl->class_obj_method_count;
545 if (extends_method_count == 0)
546 continue;
547
548 ufunc_T **extends_methods = loop == 1
549 ? extends_cl->class_class_functions
550 : extends_cl->class_obj_methods;
551
552 int method_count = loop == 1 ? classmethods_gap->ga_len
553 : objmethods_gap->ga_len;
554 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
555 ? classmethods_gap->ga_data
556 : objmethods_gap->ga_data);
557
558 for (int i = 0; i < extends_method_count; i++)
559 {
560 ufunc_T *uf = extends_methods[i];
Yegappan Lakshmanan1db15142023-09-19 20:34:05 +0200561 if (!IS_ABSTRACT_METHOD(uf))
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200562 continue;
563
Yegappan Lakshmanan68d08582025-02-09 19:39:52 +0100564 int concrete_method_found = FALSE;
565 int j = 0;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200566
Yegappan Lakshmanan68d08582025-02-09 19:39:52 +0100567 // Check if the abstract method is already implemented in one of
568 // the parent classes.
569 for (j = 0; !concrete_method_found && j < i; j++)
570 {
571 ufunc_T *uf2 = extends_methods[j];
572 if (!IS_ABSTRACT_METHOD(uf2) &&
573 STRCMP(uf->uf_name, uf2->uf_name) == 0)
574 concrete_method_found = TRUE;
575 }
576
577 if (concrete_method_found)
578 continue;
579
580 for (j = 0; j < method_count; j++)
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200581 {
582 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
583 {
Yegappan Lakshmanan68d08582025-02-09 19:39:52 +0100584 concrete_method_found = TRUE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200585 break;
586 }
587 }
588
Yegappan Lakshmanan68d08582025-02-09 19:39:52 +0100589 if (!concrete_method_found)
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200590 {
Yegappan Lakshmanan68d08582025-02-09 19:39:52 +0100591 semsg(_(e_abstract_method_str_not_implemented), uf->uf_name);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200592 return FALSE;
593 }
594 }
595 }
596
597 return TRUE;
598}
599
600/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200601 * Returns TRUE if the interface variable "if_var" is present in the list of
602 * variables in "cl_mt" or in the parent lineage of one of the extended classes
603 * in "extends_cl". For a class variable, 'is_class_var' is TRUE.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200604 */
605 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200606intf_variable_present(
607 char_u *intf_class_name,
608 ocmember_T *if_var,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200609 ocmember_T *cl_mt,
610 int cl_member_count,
611 class_T *extends_cl)
612{
613 int variable_present = FALSE;
614
615 for (int cl_i = 0; cl_i < cl_member_count; ++cl_i)
616 {
617 ocmember_T *m = &cl_mt[cl_i];
618 where_T where = WHERE_INIT;
619
620 if (STRCMP(if_var->ocm_name, m->ocm_name) != 0)
621 continue;
622
623 // Ensure the access type is same
624 if (if_var->ocm_access != m->ocm_access)
625 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200626 semsg(_(e_variable_str_of_interface_str_has_different_access),
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200627 if_var->ocm_name, intf_class_name);
628 return FALSE;
629 }
630
631 // Ensure the type is matching.
632 if (m->ocm_type == &t_any)
633 {
634 // variable type is not specified. Use the variable type in the
635 // interface.
636 m->ocm_type = if_var->ocm_type;
637 }
638 else
639 {
640 where.wt_func_name = (char *)m->ocm_name;
641 where.wt_kind = WT_MEMBER;
642 if (check_type(if_var->ocm_type, m->ocm_type, TRUE,
643 where) == FAIL)
644 return FALSE;
645 }
646
647 variable_present = TRUE;
648 break;
649 }
650
651 if (!variable_present && extends_cl != NULL)
652 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200653 int ext_cl_count = extends_cl->class_obj_member_count;
654 ocmember_T *ext_cl_mt = extends_cl->class_obj_members;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200655 return intf_variable_present(intf_class_name, if_var,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200656 ext_cl_mt, ext_cl_count,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200657 extends_cl->class_extends);
658 }
659
660 return variable_present;
661}
662
663/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200664 * Check the variables of the interface class "ifcl" match object variables
665 * ("objmembers_gap") of a class.
666 * Returns TRUE if the object variables names are valid.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200667 */
668 static int
669validate_interface_variables(
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200670 char_u *intf_class_name,
671 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200672 garray_T *objmembers_gap,
673 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200674{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200675 int if_count = ifcl->class_obj_member_count;
676 if (if_count == 0)
677 return TRUE;
678
679 ocmember_T *if_ms = ifcl->class_obj_members;
680 ocmember_T *cl_ms = (ocmember_T *)(objmembers_gap->ga_data);
681 int cl_count = objmembers_gap->ga_len;
682 for (int if_i = 0; if_i < if_count; ++if_i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200683 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200684 if (!intf_variable_present(intf_class_name, &if_ms[if_i], cl_ms,
685 cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200686 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200687 semsg(_(e_variable_str_of_interface_str_not_implemented),
688 if_ms[if_i].ocm_name, intf_class_name);
689 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200690 }
691 }
692
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200693 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200694}
695
696/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200697 * Returns TRUE if the method signature of "if_method" and "cl_method" matches.
698 */
699 static int
700intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method)
701{
702 where_T where = WHERE_INIT;
703
704 // Ensure the type is matching.
705 where.wt_func_name = (char *)if_method->uf_name;
706 where.wt_kind = WT_METHOD;
707 if (check_type(if_method->uf_func_type, cl_method->uf_func_type, TRUE,
708 where) == FAIL)
709 return FALSE;
710
711 return TRUE;
712}
713
714/*
715 * Returns TRUE if the interface method "if_ufunc" is present in the list of
716 * methods in "cl_fp" or in the parent lineage of one of the extended classes
717 * in "extends_cl". For a class method, 'is_class_method' is TRUE.
718 */
719 static int
720intf_method_present(
721 ufunc_T *if_ufunc,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200722 ufunc_T **cl_fp,
723 int cl_count,
724 class_T *extends_cl)
725{
726 int method_present = FALSE;
727
728 for (int cl_i = 0; cl_i < cl_count; ++cl_i)
729 {
730 char_u *cl_name = cl_fp[cl_i]->uf_name;
731 if (STRCMP(if_ufunc->uf_name, cl_name) == 0)
732 {
733 // Ensure the type is matching.
734 if (!intf_method_type_matches(if_ufunc, cl_fp[cl_i]))
735 return FALSE;
736 method_present = TRUE;
737 break;
738 }
739 }
740
741 if (!method_present && extends_cl != NULL)
742 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200743 ufunc_T **ext_cl_fp = (ufunc_T **)(extends_cl->class_obj_methods);
744 int ext_cl_count = extends_cl->class_obj_method_count;
745 return intf_method_present(if_ufunc, ext_cl_fp, ext_cl_count,
746 extends_cl->class_extends);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200747 }
748
749 return method_present;
750}
751
752/*
753 * Validate that a new class implements all the class/instance methods in the
754 * interface "ifcl". The new class methods are in "classfunctions_gap" and the
755 * new object methods are in "objmemthods_gap". Also validates the method
756 * types.
757 * Returns TRUE if all the interface class/object methods are implemented in
758 * the new class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200759 */
760 static int
761validate_interface_methods(
762 char_u *intf_class_name,
763 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200764 garray_T *objmethods_gap,
765 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200766{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200767 int if_count = ifcl->class_obj_method_count;
768 if (if_count == 0)
769 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200770
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200771 ufunc_T **if_fp = ifcl->class_obj_methods;
772 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
773 int cl_count = objmethods_gap->ga_len;
774 for (int if_i = 0; if_i < if_count; ++if_i)
775 {
776 char_u *if_name = if_fp[if_i]->uf_name;
777
778 if (!intf_method_present(if_fp[if_i], cl_fp, cl_count, extends_cl))
779 {
780 semsg(_(e_method_str_of_interface_str_not_implemented),
781 if_name, intf_class_name);
782 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200783 }
784 }
785
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200786 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200787}
788
789/*
790 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200791 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200792 * object methods and object members in the new class are in
793 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
794 * "objmembers_gap" respectively.
795 */
796 static int
797validate_implements_classes(
798 garray_T *impl_gap,
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +0100799 garray_T *intf_classes_gap,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200800 garray_T *objmethods_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200801 garray_T *objmembers_gap,
802 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200803{
804 int success = TRUE;
805
806 for (int i = 0; i < impl_gap->ga_len && success; ++i)
807 {
808 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
809 typval_T tv;
810 tv.v_type = VAR_UNKNOWN;
811 if (eval_variable_import(impl, &tv) == FAIL)
812 {
813 semsg(_(e_interface_name_not_found_str), impl);
814 success = FALSE;
815 break;
816 }
817
818 if (tv.v_type != VAR_CLASS
819 || tv.vval.v_class == NULL
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100820 || !IS_INTERFACE(tv.vval.v_class))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200821 {
822 semsg(_(e_not_valid_interface_str), impl);
823 success = FALSE;
824 clear_tv(&tv);
825 break;
826 }
827
828 class_T *ifcl = tv.vval.v_class;
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +0100829 if (ga_grow(intf_classes_gap, 1) == FAIL)
830 {
831 success = FALSE;
832 clear_tv(&tv);
833 break;
834 }
835 ((class_T **)intf_classes_gap->ga_data)[intf_classes_gap->ga_len]
836 = ifcl;
837 intf_classes_gap->ga_len++;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200838 ++ifcl->class_refcount;
839
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200840 // check the variables of the interface match the members of the class
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200841 success = validate_interface_variables(impl, ifcl, objmembers_gap,
842 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200843
844 // check the functions/methods of the interface match the
845 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200846 if (success)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200847 success = validate_interface_methods(impl, ifcl, objmethods_gap,
848 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200849 clear_tv(&tv);
850 }
851
852 return success;
853}
854
855/*
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +0100856 * Returns TRUE if the interface class "ifcl" is already present in the
857 * "intf_classes_gap" grow array.
858 */
859 static int
860is_interface_class_present(garray_T *intf_classes_gap, class_T *ifcl)
861{
862 for (int j = 0; j < intf_classes_gap->ga_len; j++)
863 {
864 if (((class_T **)intf_classes_gap)[j] == ifcl)
865 return TRUE;
866 }
867
868 return FALSE;
869}
870
871/*
872 * Add interface "ifcl" from a super class to "intf_classes_gap" and the class
873 * name to "impl_gap".
874 */
875 static int
876add_interface_from_super_class(
877 class_T *ifcl,
878 garray_T *impl_gap,
879 garray_T *intf_classes_gap)
880{
881 char_u *intf_name;
882
883 // Add the interface name to "impl_gap"
884 intf_name = vim_strsave(ifcl->class_name);
885 if (intf_name == NULL)
886 return FALSE;
887
888 if (ga_grow(impl_gap, 1) == FAIL)
889 return FALSE;
890
891 char_u **intf_names = (char_u **)impl_gap->ga_data;
892 intf_names[impl_gap->ga_len] = intf_name;
893 impl_gap->ga_len++;
894
895 // Add the interface class to "intf_classes_gap"
896 if (ga_grow(intf_classes_gap, 1) == FAIL)
897 return FALSE;
898
899 class_T **intf_classes = (class_T **)intf_classes_gap->ga_data;
900 intf_classes[intf_classes_gap->ga_len] = ifcl;
901 intf_classes_gap->ga_len++;
902 ++ifcl->class_refcount;
903
904 return TRUE;
905}
906
907/*
908 * Add "super" class interfaces to "intf_classes_gap" (if not present already)
909 * Add the interface class names to "impl_gap".
910 */
911 static int
912add_super_class_interfaces(
913 class_T *super,
914 garray_T *impl_gap,
915 garray_T *intf_classes_gap)
916{
917 // Iterate through all the interfaces implemented by "super"
918 for (int i = 0; i < super->class_interface_count; i++)
919 {
920 class_T *ifcl = super->class_interfaces_cl[i];
921
922 if (!is_interface_class_present(intf_classes_gap, ifcl))
923 add_interface_from_super_class(ifcl, impl_gap, intf_classes_gap);
924 }
925
926 return TRUE;
927}
928
929/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200930 * Check no function argument name is used as a class member.
931 * (Object members are always accessed with "this." prefix, so no need
932 * to check them.)
933 */
934 static int
935check_func_arg_names(
936 garray_T *classfunctions_gap,
937 garray_T *objmethods_gap,
938 garray_T *classmembers_gap)
939{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200940 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200941 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200942 {
943 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
944
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200945 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200946 {
947 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
948
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200949 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200950 {
951 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
952 garray_T *mgap = classmembers_gap;
953
954 // Check all the class member names
955 for (int mi = 0; mi < mgap->ga_len; ++mi)
956 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200957 char_u *mname =
958 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200959 if (STRCMP(aname, mname) == 0)
960 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200961 if (uf->uf_script_ctx.sc_sid > 0)
962 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
963
964 semsg(_(e_argument_already_declared_in_class_str),
965 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200966
967 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200968 }
969 }
970 }
971 }
972 }
973
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200974 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200975}
976
977/*
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200978 * Returns TRUE if 'varname' is a reserved keyword name
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200979 */
980 static int
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200981is_reserved_varname(char_u *varname, char_u *varname_end)
982{
983 int reserved = FALSE;
984 char_u save_varname_end = *varname_end;
985 *varname_end = NUL;
986
987 reserved = check_reserved_name(varname, FALSE) == FAIL;
988
989 *varname_end = save_varname_end;
990
991 return reserved;
992}
993
994/*
995 * Returns TRUE if the variable "varname" is already defined either as a class
996 * variable or as an object variable.
997 */
998 static int
999is_duplicate_variable(
1000 garray_T *class_members,
1001 garray_T *obj_members,
1002 char_u *varname,
1003 char_u *varname_end)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001004{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001005 char_u *name = vim_strnsave(varname, varname_end - varname);
1006 char_u *pstr = (*name == '_') ? name + 1 : name;
1007 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001008
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001009 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001010 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001011 // loop == 1: class variables, loop == 2: object variables
1012 garray_T *vgap = (loop == 1) ? class_members : obj_members;
1013 for (int i = 0; i < vgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001014 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001015 ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i;
1016 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1
1017 : m->ocm_name;
1018 if (STRCMP(pstr, qstr) == 0)
1019 {
1020 semsg(_(e_duplicate_variable_str), name);
1021 dup = TRUE;
1022 break;
1023 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001024 }
1025 }
1026
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001027 vim_free(name);
1028 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001029}
1030
1031/*
1032 * Returns TRUE if the method "name" is already defined.
1033 */
1034 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001035is_duplicate_method(
1036 garray_T *classmethods_gap,
1037 garray_T *objmethods_gap,
1038 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001039{
1040 char_u *pstr = (*name == '_') ? name + 1 : name;
1041
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001042 // loop 1: class methods, loop 2: object methods
1043 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001044 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001045 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
1046 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001047 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001048 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
1049 char_u *qstr = *n == '_' ? n + 1 : n;
1050 if (STRCMP(pstr, qstr) == 0)
1051 {
1052 semsg(_(e_duplicate_function_str), name);
1053 return TRUE;
1054 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001055 }
1056 }
1057
1058 return FALSE;
1059}
1060
1061/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001062 * Returns TRUE if the constructor is valid.
1063 */
1064 static int
1065is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
1066{
1067 // Constructors are not allowed in abstract classes.
1068 if (is_abstract)
1069 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001070 emsg(_(e_cannot_define_new_method_in_abstract_class));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001071 return FALSE;
1072 }
1073 // A constructor is always static, no need to define it so.
1074 if (has_static)
1075 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001076 emsg(_(e_cannot_define_new_method_as_static));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001077 return FALSE;
1078 }
1079 // A return type should not be specified for the new()
1080 // constructor method.
1081 if (uf->uf_ret_type->tt_type != VAR_VOID)
1082 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001083 emsg(_(e_cannot_use_a_return_type_with_new_method));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001084 return FALSE;
1085 }
1086 return TRUE;
1087}
1088
1089/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001090 * Returns TRUE if 'uf' is a supported builtin method and has the correct
1091 * method signature.
1092 */
1093 static int
1094object_check_builtin_method_sig(ufunc_T *uf)
1095{
1096 char_u *name = uf->uf_name;
1097 int valid = FALSE;
1098 type_T method_sig;
1099 type_T method_rt;
1100 where_T where = WHERE_INIT;
1101
1102 // validate the method signature
1103 CLEAR_FIELD(method_sig);
1104 CLEAR_FIELD(method_rt);
1105 method_sig.tt_type = VAR_FUNC;
1106
1107 if (STRCMP(name, "len") == 0)
1108 {
1109 // def __len(): number
1110 method_rt.tt_type = VAR_NUMBER;
1111 method_sig.tt_member = &method_rt;
1112 valid = TRUE;
1113 }
1114 else if (STRCMP(name, "empty") == 0)
1115 {
1116 // def __empty(): bool
1117 method_rt.tt_type = VAR_BOOL;
1118 method_sig.tt_member = &method_rt;
1119 valid = TRUE;
1120 }
1121 else if (STRCMP(name, "string") == 0)
1122 {
1123 // def __string(): string
1124 method_rt.tt_type = VAR_STRING;
1125 method_sig.tt_member = &method_rt;
1126 valid = TRUE;
1127 }
1128 else
1129 semsg(_(e_builtin_object_method_str_not_supported), uf->uf_name);
1130
1131 where.wt_func_name = (char *)uf->uf_name;
1132 where.wt_kind = WT_METHOD;
1133 if (valid && !check_type(&method_sig, uf->uf_func_type, TRUE, where))
1134 valid = FALSE;
1135
1136 return valid;
1137}
1138
1139/*
1140 * Returns TRUE if "funcname" is a supported builtin object method name
1141 */
1142 int
1143is_valid_builtin_obj_methodname(char_u *funcname)
1144{
1145 switch (funcname[0])
1146 {
1147 case 'e':
1148 return STRNCMP(funcname, "empty", 5) == 0;
1149
1150 case 'l':
1151 return STRNCMP(funcname, "len", 3) == 0;
1152
1153 case 'n':
1154 return STRNCMP(funcname, "new", 3) == 0;
1155
1156 case 's':
1157 return STRNCMP(funcname, "string", 6) == 0;
1158 }
1159
1160 return FALSE;
1161}
1162
1163
1164/*
1165 * Returns the builtin method "name" in object "obj". Returns NULL if the
1166 * method is not found.
1167 */
1168 ufunc_T *
1169class_get_builtin_method(
1170 class_T *cl,
1171 class_builtin_T builtin_method,
1172 int *method_idx)
1173{
1174 *method_idx = -1;
1175
1176 if (cl == NULL)
1177 return NULL;
1178
1179 *method_idx = cl->class_builtin_methods[builtin_method];
1180 return *method_idx != -1 ? cl->class_obj_methods[*method_idx] : NULL;
1181}
1182
1183/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001184 * Update the interface class lookup table for the member index on the
1185 * interface to the member index in the class implementing the interface.
1186 * And a lookup table for the object method index on the interface
1187 * to the object method index in the class implementing the interface.
1188 * This is also used for updating the lookup table for the extended class
1189 * hierarchy.
1190 */
1191 static int
1192update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001193 class_T *ifcl,
1194 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001195 garray_T *objmethods,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001196 int pobj_method_offset)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001197{
1198 if (ifcl == NULL)
1199 return OK;
1200
1201 // Table for members.
1202 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001203 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001204 if (if2cl == NULL)
1205 return FAIL;
1206 if2cl->i2c_next = ifcl->class_itf2class;
1207 ifcl->class_itf2class = if2cl;
1208 if2cl->i2c_class = cl;
1209 if2cl->i2c_is_method = FALSE;
1210
1211 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
1212 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
1213 {
1214 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001215 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001216 {
1217 int *table = (int *)(if2cl + 1);
1218 table[if_i] = cl_i;
1219 break;
1220 }
1221 }
1222
1223 // Table for methods.
1224 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001225 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001226 if (if2cl == NULL)
1227 return FAIL;
1228 if2cl->i2c_next = ifcl->class_itf2class;
1229 ifcl->class_itf2class = if2cl;
1230 if2cl->i2c_class = cl;
1231 if2cl->i2c_is_method = TRUE;
1232
1233 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
1234 {
1235 int done = FALSE;
1236 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
1237 {
1238 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001239 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001240 {
1241 int *table = (int *)(if2cl + 1);
1242 table[if_i] = cl_i;
1243 done = TRUE;
1244 break;
1245 }
1246 }
1247
1248 // extended class object method is not overridden by the child class.
1249 // Keep the method declared in one of the parent classes in the
1250 // lineage.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001251 if (!done)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001252 {
1253 // If "ifcl" is not the immediate parent of "cl", then search in
1254 // the intermediate parent classes.
1255 if (cl->class_extends != ifcl)
1256 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001257 class_T *parent = cl->class_extends;
1258 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001259
1260 while (!done && parent != NULL && parent != ifcl)
1261 {
1262
1263 for (int cl_i = 0;
1264 cl_i < parent->class_obj_method_count_child; ++cl_i)
1265 {
1266 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
1267 parent->class_obj_methods[cl_i]->uf_name)
1268 == 0)
1269 {
1270 int *table = (int *)(if2cl + 1);
1271 table[if_i] = method_offset + cl_i;
1272 done = TRUE;
1273 break;
1274 }
1275 }
1276 method_offset += parent->class_obj_method_count_child;
1277 parent = parent->class_extends;
1278 }
1279 }
1280
1281 if (!done)
1282 {
1283 int *table = (int *)(if2cl + 1);
1284 table[if_i] = pobj_method_offset + if_i;
1285 }
1286 }
1287 }
1288
1289 return OK;
1290}
1291
1292/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001293 * Update the member and object method lookup tables for a new class in the
1294 * interface class.
1295 * For each interface add a lookup table for the member index on the interface
1296 * to the member index in the new class. And a lookup table for the object
1297 * method index on the interface to the object method index in the new class.
1298 */
1299 static int
1300add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
1301{
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001302 // update the lookup table for all the implemented interfaces
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001303 for (int i = 0; i < cl->class_interface_count; ++i)
1304 {
1305 class_T *ifcl = cl->class_interfaces_cl[i];
1306
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001307 // update the lookup table for this interface and all its super
1308 // interfaces.
1309 while (ifcl != NULL)
1310 {
1311 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
1312 0) == FAIL)
1313 return FAIL;
1314 ifcl = ifcl->class_extends;
1315 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001316 }
1317
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001318 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001319 if (extends_cl != NULL)
1320 {
1321 class_T *pclass = extends_cl;
1322 int pobj_method_offset = objmethods_gap->ga_len;
1323
1324 // Update the entire lineage of extended classes.
1325 while (pclass != NULL)
1326 {
1327 if (update_member_method_lookup_table(pclass, cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001328 objmethods_gap, pobj_method_offset) == FAIL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001329 return FAIL;
1330
1331 pobj_method_offset += pclass->class_obj_method_count_child;
1332 pclass = pclass->class_extends;
1333 }
1334 }
1335
1336 return OK;
1337}
1338
1339/*
1340 * Add class members to a new class. Allocate a typval for each class member
1341 * and initialize it.
1342 */
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001343 static int
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001344add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001345{
1346 // Allocate a typval for each class member and initialize it.
1347 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
1348 cl->class_class_member_count);
1349 if (cl->class_members_tv == NULL)
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001350 return FAIL;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001351
1352 for (int i = 0; i < cl->class_class_member_count; ++i)
1353 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001354 ocmember_T *m = &cl->class_class_members[i];
1355 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001356 if (m->ocm_init != NULL)
1357 {
1358 typval_T *etv = eval_expr(m->ocm_init, eap);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001359 if (etv == NULL)
1360 return FAIL;
1361
1362 if (m->ocm_type->tt_type == VAR_ANY
1363 && !(m->ocm_flags & OCMFLAG_HAS_TYPE)
1364 && etv->v_type != VAR_SPECIAL)
1365 // If the member variable type is not yet set, then use
1366 // the initialization expression type.
1367 m->ocm_type = typval2type(etv, get_copyID(),
1368 type_list_gap,
1369 TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
1370 *tv = *etv;
1371 vim_free(etv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001372 }
1373 else
1374 {
1375 // TODO: proper default value
1376 tv->v_type = m->ocm_type->tt_type;
1377 tv->vval.v_string = NULL;
1378 }
LemonBoyf4af3312024-07-04 13:43:12 +02001379 set_tv_type(tv, m->ocm_type);
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001380 if (m->ocm_flags & OCMFLAG_CONST)
1381 item_lock(tv, DICT_MAXNEST, TRUE, TRUE);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001382 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001383
1384 return OK;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001385}
1386
1387/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001388 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001389 */
1390 static void
1391add_default_constructor(
1392 class_T *cl,
1393 garray_T *classfunctions_gap,
1394 garray_T *type_list_gap)
1395{
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001396 garray_T fga;
1397 int is_enum = IS_ENUM(cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001398
1399 ga_init2(&fga, 1, 1000);
1400 ga_concat(&fga, (char_u *)"new(");
1401 for (int i = 0; i < cl->class_obj_member_count; ++i)
1402 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001403 if (i < 2 && is_enum)
1404 // The first two object variables in an enum are the enum value
1405 // name and ordinal. Don't initialize these object variables in
1406 // the default constructor as they are already initialized right
1407 // after creating the object.
1408 continue;
1409
1410 if (i > (is_enum ? 2 : 0))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001411 ga_concat(&fga, (char_u *)", ");
1412 ga_concat(&fga, (char_u *)"this.");
1413 ocmember_T *m = cl->class_obj_members + i;
1414 ga_concat(&fga, (char_u *)m->ocm_name);
1415 ga_concat(&fga, (char_u *)" = v:none");
1416 }
1417 ga_concat(&fga, (char_u *)")\nenddef\n");
1418 ga_append(&fga, NUL);
1419
1420 exarg_T fea;
1421 CLEAR_FIELD(fea);
1422 fea.cmdidx = CMD_def;
1423 fea.cmd = fea.arg = fga.ga_data;
1424
1425 garray_T lines_to_free;
1426 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1427
h-eastb895b0f2023-09-24 15:46:31 +02001428 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
1429 cl->class_obj_members, cl->class_obj_member_count);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001430
1431 ga_clear_strings(&lines_to_free);
1432 vim_free(fga.ga_data);
1433
1434 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1435 {
1436 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001437 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001438 ++classfunctions_gap->ga_len;
1439
1440 nf->uf_flags |= FC_NEW;
1441 nf->uf_ret_type = get_type_ptr(type_list_gap);
1442 if (nf->uf_ret_type != NULL)
1443 {
1444 nf->uf_ret_type->tt_type = VAR_OBJECT;
1445 nf->uf_ret_type->tt_class = cl;
1446 nf->uf_ret_type->tt_argcount = 0;
1447 nf->uf_ret_type->tt_args = NULL;
1448 }
1449 }
1450}
1451
1452/*
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001453 * Add the class methods and object methods to the new class "cl".
1454 * When extending a class "extends_cl", add the instance methods from the
1455 * parent class also.
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001456 * Returns OK on success and FAIL on memory allocation failure.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001457 */
1458 static int
1459add_classfuncs_objmethods(
1460 class_T *cl,
1461 class_T *extends_cl,
1462 garray_T *classfunctions_gap,
1463 garray_T *objmethods_gap)
1464{
1465 // loop 1: class functions, loop 2: object methods
1466 for (int loop = 1; loop <= 2; ++loop)
1467 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001468 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1469 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001470 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001471 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001472 : &cl->class_obj_methods;
1473
1474 int parent_count = 0;
1475 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001476 // Include object methods from the parent.
1477 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001478 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001479 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001480 : extends_cl->class_obj_method_count;
1481
1482 *fcount = parent_count + gap->ga_len;
1483 if (*fcount == 0)
1484 {
1485 *fup = NULL;
1486 continue;
1487 }
1488 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1489 if (*fup == NULL)
1490 return FAIL;
1491
1492 if (gap->ga_len != 0)
1493 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001494 VIM_CLEAR(gap->ga_data);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001495 if (loop == 1)
1496 cl->class_class_function_count_child = gap->ga_len;
1497 else
1498 cl->class_obj_method_count_child = gap->ga_len;
1499
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001500 if (loop == 2)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001501 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001502 // Copy instance methods from the parent.
1503
1504 for (int i = 0; i < parent_count; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001505 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001506 // Can't use the same parent function, because "uf_class" is
1507 // different and compilation will have a different result.
1508 // Put them after the functions in the current class, object
1509 // methods may be overruled, then "super.Method()" is used to
1510 // find a method from the parent.
1511 ufunc_T *pf = (extends_cl->class_obj_methods)[i];
1512 (*fup)[gap->ga_len + i] = copy_function(pf);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001513
1514 // If the child class overrides a function from the parent
1515 // the signature must be equal.
1516 char_u *pname = pf->uf_name;
1517 for (int ci = 0; ci < gap->ga_len; ++ci)
1518 {
1519 ufunc_T *cf = (*fup)[ci];
1520 char_u *cname = cf->uf_name;
1521 if (STRCMP(pname, cname) == 0)
1522 {
1523 where_T where = WHERE_INIT;
1524 where.wt_func_name = (char *)pname;
1525 where.wt_kind = WT_METHOD;
1526 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1527 TRUE, where);
1528 }
1529 }
1530 }
1531 }
1532
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001533 // Set the class pointer on all the functions and object methods.
1534 for (int i = 0; i < *fcount; ++i)
1535 {
1536 ufunc_T *fp = (*fup)[i];
1537 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001538 if (i < gap->ga_len)
1539 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001540 if (loop == 2)
1541 fp->uf_flags |= FC_OBJECT;
1542 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001543
1544 ga_clear(gap);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001545 }
1546
1547 return OK;
1548}
1549
1550/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001551 * Update the index of object methods called by builtin functions.
1552 */
1553 static void
1554update_builtin_method_index(class_T *cl)
1555{
1556 int i;
1557
1558 for (i = 0; i < CLASS_BUILTIN_MAX; i++)
1559 cl->class_builtin_methods[i] = -1;
1560
1561 for (i = 0; i < cl->class_obj_method_count; i++)
1562 {
1563 ufunc_T *uf = cl->class_obj_methods[i];
1564
1565 if (cl->class_builtin_methods[CLASS_BUILTIN_STRING] == -1
1566 && STRCMP(uf->uf_name, "string") == 0)
1567 cl->class_builtin_methods[CLASS_BUILTIN_STRING] = i;
1568 else if (cl->class_builtin_methods[CLASS_BUILTIN_EMPTY] == -1 &&
1569 STRCMP(uf->uf_name, "empty") == 0)
1570 cl->class_builtin_methods[CLASS_BUILTIN_EMPTY] = i;
1571 else if (cl->class_builtin_methods[CLASS_BUILTIN_LEN] == -1 &&
1572 STRCMP(uf->uf_name, "len") == 0)
1573 cl->class_builtin_methods[CLASS_BUILTIN_LEN] = i;
1574 }
1575}
1576
1577/*
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001578 * Return the end of the class name starting at "arg". Valid characters in a
1579 * class name are alphanumeric characters and "_". Also handles imported class
1580 * names.
1581 */
1582 static char_u *
1583find_class_name_end(char_u *arg)
1584{
1585 char_u *end = arg;
1586
1587 while (ASCII_ISALNUM(*end) || *end == '_'
1588 || (*end == '.' && (ASCII_ISALNUM(end[1]) || end[1] == '_')))
1589 ++end;
1590
1591 return end;
1592}
1593
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001594/*
1595 * Returns TRUE if the enum value "varname" is already defined.
1596 */
1597 static int
1598is_duplicate_enum(
1599 garray_T *enum_gap,
1600 char_u *varname,
1601 char_u *varname_end)
1602{
1603 char_u *name = vim_strnsave(varname, varname_end - varname);
1604 int dup = FALSE;
1605
1606 for (int i = 0; i < enum_gap->ga_len; ++i)
1607 {
1608 ocmember_T *m = ((ocmember_T *)enum_gap->ga_data) + i;
1609 if (STRCMP(name, m->ocm_name) == 0)
1610 {
1611 semsg(_(e_duplicate_enum_str), name);
1612 dup = TRUE;
1613 break;
1614 }
1615 }
1616
1617 vim_free(name);
1618 return dup;
1619}
1620
1621/*
1622 * Parse the enum values in "line" separated by comma and add them to "gap".
1623 * If the last enum value is found, then "enum_end" is set to TRUE.
1624 */
1625 static int
1626enum_parse_values(
1627 exarg_T *eap,
1628 class_T *en,
1629 char_u *line,
1630 garray_T *gap,
1631 int *num_enum_values,
1632 int *enum_end)
1633{
1634 evalarg_T evalarg;
1635 char_u *p = line;
1636 char initexpr_buf[1024];
1637 char_u last_char = NUL;
1638 int rc = OK;
1639
1640 fill_evalarg_from_eap(&evalarg, eap, FALSE);
1641
1642 int did_emsg_before = did_emsg;
1643 while (*p != NUL)
1644 {
1645 // ignore comment
1646 if (*p == '#')
1647 break;
1648
1649 if (!eval_isnamec1(*p))
1650 {
1651 semsg(_(e_invalid_enum_value_declaration_str), p);
1652 break;
1653 }
1654
1655 char_u *eni_name_start = p;
1656 char_u *eni_name_end = to_name_end(p, FALSE);
1657
1658 if (is_duplicate_enum(gap, eni_name_start, eni_name_end))
1659 break;
1660
1661 p = skipwhite(eni_name_end);
1662
1663 char_u *init_expr = NULL;
1664 if (*p == '(')
1665 {
1666 if (VIM_ISWHITE(p[-1]))
1667 {
1668 semsg(_(e_no_white_space_allowed_before_str_str), "(", line);
1669 break;
1670 }
1671
1672 char_u *expr_start, *expr_end;
1673
1674 p = eni_name_start;
1675 (void)skip_expr_concatenate(&p, &expr_start, &expr_end, &evalarg);
1676
1677 while (*expr_start && *expr_start != '(')
1678 expr_start++;
1679
1680 if (expr_end > expr_start)
1681 init_expr = vim_strnsave(expr_start, expr_end - expr_start);
1682 }
1683
1684 if (init_expr == NULL)
1685 vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new()",
1686 en->class_name);
1687 else
1688 {
1689 vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new%s",
1690 en->class_name, init_expr);
1691 vim_free(init_expr);
1692 }
1693 if (add_member(gap, eni_name_start, eni_name_end, FALSE,
1694 TRUE, TRUE, TRUE, &en->class_object_type,
1695 vim_strsave((char_u *)initexpr_buf)) == FAIL)
1696 break;
1697
1698 ++*num_enum_values;
1699
1700 if (*p != '#')
1701 last_char = *p;
1702
1703 if (*p != NUL && *p != ',')
1704 break;
1705
1706 if (*p == ',')
1707 {
1708 if (!IS_WHITE_OR_NUL(p[1]))
1709 {
1710 semsg(_(e_white_space_required_after_str_str), ",", line);
1711 break;
1712 }
1713 if (VIM_ISWHITE(p[-1]))
1714 {
1715 semsg(_(e_no_white_space_allowed_before_str_str), ",", line);
1716 break;
1717 }
1718 p = skipwhite(p + 1);
1719 }
1720 }
1721
Doug Kearnsdbe39ed2025-01-04 17:12:24 +01001722 p = skipwhite(p);
1723
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001724 if (*p != NUL && *p != '#')
1725 {
1726 if (did_emsg == did_emsg_before)
1727 semsg(_(e_missing_comma_before_argument_str), p);
1728 rc = FAIL;
1729 }
1730
1731 if (last_char != ',')
1732 // last enum value should not be terminated by ","
1733 *enum_end = TRUE;
1734
1735 // Free the memory pointed by expr_start.
1736 clear_evalarg(&evalarg, NULL);
1737
1738 return rc;
1739}
1740
1741/*
1742 * Add the "values" class variable (List of enum value objects) to the enum
1743 * class "en"
1744 */
1745 static int
1746enum_add_values_member(
1747 class_T *en,
1748 garray_T *gap,
1749 int num_enum_values,
1750 garray_T *type_list_gap)
1751{
1752 garray_T fga;
1753 int rc = FAIL;
1754
1755 ga_init2(&fga, 1, 1000);
1756 ga_concat(&fga, (char_u *)"[");
1757 for (int i = 0; i < num_enum_values; ++i)
1758 {
1759 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
1760
1761 if (i > 0)
1762 ga_concat(&fga, (char_u *)", ");
1763 ga_concat(&fga, en->class_name);
1764 ga_concat(&fga, (char_u *)".");
1765 ga_concat(&fga, (char_u *)m->ocm_name);
1766 }
1767 ga_concat(&fga, (char_u *)"]");
1768 ga_append(&fga, NUL);
1769
1770 char_u *varname = (char_u *)"values";
1771
1772 type_T *type = get_type_ptr(type_list_gap);
1773 if (type == NULL)
1774 goto done;
1775
1776 type->tt_type = VAR_LIST;
1777 type->tt_member = get_type_ptr(type_list_gap);
1778 if (type->tt_member != NULL)
1779 {
1780 type->tt_member->tt_type = VAR_OBJECT;
1781 type->tt_member->tt_class = en;
1782 }
1783
1784 rc = add_member(gap, varname, varname + 6, FALSE, FALSE, TRUE, TRUE, type,
1785 vim_strsave((char_u *)fga.ga_data));
1786
1787done:
1788 vim_free(fga.ga_data);
1789
1790 return rc;
1791}
1792
1793/*
1794 * Clear the constructor method names in a enum class, so that an enum class
1795 * cannot be instantiated.
1796 */
1797 static void
1798enum_clear_constructors(class_T *en)
1799{
1800 for (int i = 0; i < en->class_class_function_count; ++i)
1801 {
1802 ufunc_T *fp = en->class_class_functions[i];
1803
1804 if (fp->uf_flags & FC_NEW)
1805 *fp->uf_name = NUL;
1806 }
1807}
1808
1809/*
1810 * Initialize the name and ordinal object variable in the enum value "enval" in
1811 * the enum "en". These values are set during the enum value object creation.
1812 */
1813 void
1814enum_set_internal_obj_vars(class_T *en, object_T *enval)
1815{
1816 int i;
1817
1818 for (i = 0; i < en->class_class_member_count; ++i)
1819 {
1820 typval_T *en_tv = en->class_members_tv + i;
1821 if (en_tv != NULL && en_tv->v_type == VAR_UNKNOWN)
1822 break;
1823 }
1824
1825 // First object variable is the name
1826 ocmember_T *value_ocm = en->class_class_members + i;
1827 typval_T *name_tv = (typval_T *)(enval + 1);
1828 name_tv->v_type = VAR_STRING;
1829 name_tv->vval.v_string = vim_strsave(value_ocm->ocm_name);
1830
1831 // Second object variable is the ordinal
1832 typval_T *ord_tv = (typval_T *)(name_tv + 1);
1833 ord_tv->v_type = VAR_NUMBER;
1834 ord_tv->vval.v_number = i;
1835}
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001836
1837/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001838 * Handle ":class" and ":abstract class" up to ":endclass".
h-eastaa979c72025-01-03 10:19:45 +01001839 * Handle ":enum" up to ":endenum".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001840 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001841 */
1842 void
1843ex_class(exarg_T *eap)
1844{
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001845 int is_class = eap->cmdidx == CMD_class;
1846 int is_abstract = eap->cmdidx == CMD_abstract;
1847 int is_enum = eap->cmdidx == CMD_enum;
1848 int is_interface;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001849 long start_lnum = SOURCING_LNUM;
1850 char_u *arg = eap->arg;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001851
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001852 if (is_abstract)
1853 {
1854 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1855 {
1856 semsg(_(e_invalid_argument_str), arg);
1857 return;
1858 }
1859 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001860 is_class = TRUE;
1861 }
1862
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001863 is_interface = !is_class && !is_enum;
1864
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001865 if (!current_script_is_vim9()
1866 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001867 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001868 {
1869 if (is_class)
1870 emsg(_(e_class_can_only_be_defined_in_vim9_script));
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001871 else if (is_enum)
1872 emsg(_(e_enum_can_only_be_defined_in_vim9_script));
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001873 else
1874 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1875 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001876 }
1877
1878 if (!ASCII_ISUPPER(*arg))
1879 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001880 if (is_class)
1881 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001882 else if (is_enum)
1883 semsg(_(e_enum_name_must_start_with_uppercase_letter_str), arg);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001884 else
1885 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1886 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001887 return;
1888 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001889 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1890 if (!IS_WHITE_OR_NUL(*name_end))
1891 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001892 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001893 return;
1894 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001895 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001896
1897 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001898 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001899
Bram Moolenaar83677162023-01-08 19:54:10 +00001900 // Name for "extends BaseClass"
1901 char_u *extends = NULL;
1902
Bram Moolenaar94674f22023-01-06 18:42:20 +00001903 // Names for "implements SomeInterface"
1904 garray_T ga_impl;
1905 ga_init2(&ga_impl, sizeof(char_u *), 5);
1906
1907 arg = skipwhite(name_end);
1908 while (*arg != NUL && *arg != '#' && *arg != '\n')
1909 {
1910 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001911 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001912 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1913 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001914 if (is_enum)
1915 {
1916 emsg(_(e_enum_cannot_extend_class));
1917 goto early_ret;
1918 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001919 if (extends != NULL)
1920 {
1921 emsg(_(e_duplicate_extends));
1922 goto early_ret;
1923 }
1924 arg = skipwhite(arg + 7);
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001925
1926 char_u *end = find_class_name_end(arg);
Bram Moolenaar83677162023-01-08 19:54:10 +00001927 if (!IS_WHITE_OR_NUL(*end))
1928 {
1929 semsg(_(e_white_space_required_after_name_str), arg);
1930 goto early_ret;
1931 }
1932 extends = vim_strnsave(arg, end - arg);
1933 if (extends == NULL)
1934 goto early_ret;
1935
1936 arg = skipwhite(end + 1);
1937 }
1938 else if (STRNCMP(arg, "implements", 10) == 0
1939 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001940 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001941 if (is_interface)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001942 {
1943 emsg(_(e_interface_cannot_use_implements));
1944 goto early_ret;
1945 }
1946
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001947 if (ga_impl.ga_len > 0)
1948 {
1949 emsg(_(e_duplicate_implements));
1950 goto early_ret;
1951 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001952 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001953
1954 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001955 {
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001956 char_u *impl_end = find_class_name_end(arg);
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001957 if ((!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1958 || (*impl_end == ','
1959 && !IS_WHITE_OR_NUL(*(impl_end + 1))))
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001960 {
1961 semsg(_(e_white_space_required_after_name_str), arg);
1962 goto early_ret;
1963 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001964 if (impl_end - arg == 0)
1965 {
1966 emsg(_(e_missing_name_after_implements));
1967 goto early_ret;
1968 }
1969
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001970 char_u *iname = vim_strnsave(arg, impl_end - arg);
1971 if (iname == NULL)
1972 goto early_ret;
1973 for (int i = 0; i < ga_impl.ga_len; ++i)
1974 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1975 {
1976 semsg(_(e_duplicate_interface_after_implements_str),
1977 iname);
1978 vim_free(iname);
1979 goto early_ret;
1980 }
1981 if (ga_add_string(&ga_impl, iname) == FAIL)
1982 {
1983 vim_free(iname);
1984 goto early_ret;
1985 }
1986 if (*impl_end != ',')
1987 {
1988 arg = skipwhite(impl_end);
1989 break;
1990 }
1991 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001992 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001993 }
1994 else
1995 {
1996 semsg(_(e_trailing_characters_str), arg);
1997early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001998 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001999 ga_clear_strings(&ga_impl);
2000 return;
2001 }
2002 }
2003
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002004 garray_T type_list; // list of pointers to allocated types
2005 ga_init2(&type_list, sizeof(type_T *), 10);
2006
Bram Moolenaard505d172022-12-18 21:42:55 +00002007 // Growarray with class members declared in the class.
2008 garray_T classmembers;
2009 ga_init2(&classmembers, sizeof(ocmember_T), 10);
2010
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002011 // Growarray with functions declared in the class.
2012 garray_T classfunctions;
2013 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00002014
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002015 // Growarray with object members declared in the class.
2016 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00002017 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002018
2019 // Growarray with object methods declared in the class.
2020 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002021 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002022
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002023 class_T *cl = NULL;
2024 class_T *extends_cl = NULL; // class from "extends" argument
2025 class_T **intf_classes = NULL;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002026 int num_enum_values = 0;
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002027
2028 cl = ALLOC_CLEAR_ONE(class_T);
2029 if (cl == NULL)
2030 goto cleanup;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002031
2032 if (is_enum)
2033 cl->class_flags = CLASS_ENUM;
2034 else if (is_interface)
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002035 cl->class_flags = CLASS_INTERFACE;
2036 else if (is_abstract)
2037 cl->class_flags = CLASS_ABSTRACT;
2038
2039 cl->class_refcount = 1;
2040 cl->class_name = vim_strnsave(name_start, name_end - name_start);
2041 if (cl->class_name == NULL)
2042 goto cleanup;
2043
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002044 cl->class_type.tt_type = VAR_CLASS;
2045 cl->class_type.tt_class = cl;
2046 cl->class_object_type.tt_type = VAR_OBJECT;
2047 cl->class_object_type.tt_class = cl;
2048
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002049 // Add the class to the script-local variables.
2050 // TODO: handle other context, e.g. in a function
2051 // TODO: does uf_hash need to be cleared?
2052 typval_T tv;
2053 tv.v_type = VAR_CLASS;
2054 tv.vval.v_class = cl;
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002055 SOURCING_LNUM = start_lnum;
Yegappan Lakshmanane3fed482025-02-20 22:20:54 +01002056 int rc = set_var_const(cl->class_name, current_sctx.sc_sid, NULL, &tv, FALSE, 0, 0);
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002057 if (rc == FAIL)
2058 goto cleanup;
2059
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002060 if (is_enum)
2061 {
2062 // All the enum classes have the name and ordinal object variables.
2063 char_u *varname = (char_u *)"name";
2064 if (add_member(&objmembers, varname, varname + 4, FALSE, FALSE, TRUE,
2065 TRUE, &t_string, NULL) == FAIL)
2066 goto cleanup;
2067
2068 varname = (char_u *)"ordinal";
2069 if (add_member(&objmembers, varname, varname + 7, FALSE, FALSE, TRUE,
2070 TRUE, &t_number, NULL) == FAIL)
2071 goto cleanup;
2072 }
2073
2074 // "export class" gets used when creating the class, don't use "is_export"
2075 // for the items inside the class.
2076 is_export = FALSE;
2077
2078 // When parsing an enum definition, this denotes whether all the enumerated
2079 // values are parsed or not.
2080 int enum_end = FALSE;
2081
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002082 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00002083 * Go over the body of the class/interface until "endclass" or
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002084 * "endinterface" or "endenum" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002085 */
2086 char_u *theline = NULL;
2087 int success = FALSE;
2088 for (;;)
2089 {
2090 vim_free(theline);
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002091 theline = eap->ea_getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002092 if (theline == NULL)
2093 break;
2094 char_u *line = skipwhite(theline);
2095
Bram Moolenaar418b5472022-12-20 13:38:22 +00002096 // Skip empty and comment lines.
2097 if (*line == NUL)
2098 continue;
2099 if (*line == '#')
2100 {
2101 if (vim9_bad_comment(line))
2102 break;
2103 continue;
2104 }
2105
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002106 char_u *p = line;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002107
2108 char *end_name;
2109 int shortlen;
2110 int fullen;
2111 if (is_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002112 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002113 end_name = "endclass";
2114 shortlen = 4;
2115 fullen = 8;
2116 }
2117 else if (is_enum)
2118 {
2119 end_name = "endenum";
2120 shortlen = 4;
2121 fullen = 7;
2122 }
2123 else
2124 {
2125 end_name = "endinterface";
2126 shortlen = 5;
2127 fullen = 12;
2128 }
2129
2130 if (checkforcmd(&p, end_name, shortlen))
2131 {
2132 if (STRNCMP(line, end_name, fullen) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002133 semsg(_(e_command_cannot_be_shortened_str), line);
2134 else if (*p == '|' || !ends_excmd2(line, p))
2135 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00002136 else
2137 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002138 break;
2139 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002140
2141 int wrong_endname = FALSE;
2142 if (is_class)
2143 wrong_endname = checkforcmd(&p, "endinterface", 5)
2144 || checkforcmd(&p, "endenum", 4);
2145 else if (is_enum)
2146 wrong_endname = checkforcmd(&p, "endclass", 4)
2147 || checkforcmd(&p, "endinterface", 5);
2148 else
2149 wrong_endname = checkforcmd(&p, "endclass", 4)
2150 || checkforcmd(&p, "endenum", 4);
2151 if (wrong_endname)
Bram Moolenaar554d0312023-01-05 19:59:18 +00002152 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00002153 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00002154 break;
2155 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002156
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002157 if (is_enum && !enum_end)
2158 {
2159 // In an enum, all the enumerated values are at the beginning
2160 // separated by comma. The class and object variables/methods
2161 // follow the values.
2162 if (enum_parse_values(eap, cl, line, &classmembers,
2163 &num_enum_values, &enum_end) == FAIL)
2164 break;
Yegappan Lakshmananabedca92024-03-29 10:08:23 +01002165
2166 if (enum_end)
2167 // Add the enum "values" class variable.
2168 enum_add_values_member(cl, &classmembers, num_enum_values,
2169 &type_list);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002170 continue;
2171 }
2172
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002173 int has_public = FALSE;
2174 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002175 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002176 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002177 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002178 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002179 break;
2180 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002181 if (is_interface)
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002182 {
Yegappan Lakshmananb90e3bc2023-09-28 23:06:48 +02002183 emsg(_(e_public_variable_not_supported_in_interface));
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002184 break;
2185 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002186 has_public = TRUE;
2187 p = skipwhite(line + 6);
2188
Yegappan Lakshmananc51578f2024-04-13 17:58:09 +02002189 if (STRNCMP(p, "def", 3) == 0)
2190 {
2191 emsg(_(e_public_keyword_not_supported_for_method));
2192 break;
2193 }
2194
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002195 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "static", 6) != 0
2196 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002197 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002198 emsg(_(e_public_must_be_followed_by_var_static_final_or_const));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002199 break;
2200 }
2201 }
Bram Moolenaard505d172022-12-18 21:42:55 +00002202
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002203 int abstract_method = FALSE;
2204 char_u *pa = p;
2205 if (checkforcmd(&p, "abstract", 3))
2206 {
2207 if (STRNCMP(pa, "abstract", 8) != 0)
2208 {
2209 semsg(_(e_command_cannot_be_shortened_str), pa);
2210 break;
2211 }
2212
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002213 if (is_enum)
2214 {
2215 // "abstract" not supported in an enum
2216 emsg(_(e_abstract_cannot_be_used_in_enum));
2217 break;
2218 }
2219
2220 if (is_interface)
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01002221 {
2222 // "abstract" not supported in an interface
2223 emsg(_(e_abstract_cannot_be_used_in_interface));
2224 break;
2225 }
2226
2227 if (!is_abstract)
2228 {
2229 semsg(_(e_abstract_method_in_concrete_class), pa);
2230 break;
2231 }
2232
Yegappan Lakshmanan5a539252023-11-04 09:42:46 +01002233 p = skipwhite(pa + 8);
2234 if (STRNCMP(p, "def", 3) != 0)
2235 {
2236 emsg(_(e_abstract_must_be_followed_by_def));
2237 break;
2238 }
2239
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01002240 abstract_method = TRUE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002241 }
2242
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002243 int has_static = FALSE;
2244 char_u *ps = p;
2245 if (checkforcmd(&p, "static", 4))
2246 {
2247 if (STRNCMP(ps, "static", 6) != 0)
2248 {
2249 semsg(_(e_command_cannot_be_shortened_str), ps);
2250 break;
2251 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002252
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002253 if (is_interface)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002254 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002255 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002256 break;
2257 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002258 has_static = TRUE;
2259 p = skipwhite(ps + 6);
Doug Kearns74da0ee2023-12-14 20:26:26 +01002260
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002261 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "def", 3) != 0
2262 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Doug Kearns74da0ee2023-12-14 20:26:26 +01002263 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002264 emsg(_(e_static_must_be_followed_by_var_def_final_or_const));
Doug Kearns74da0ee2023-12-14 20:26:26 +01002265 break;
2266 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002267 }
2268
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002269 int has_final = FALSE;
2270 int has_var = FALSE;
2271 int has_const = FALSE;
2272 if (checkforcmd(&p, "var", 3))
2273 has_var = TRUE;
2274 else if (checkforcmd(&p, "final", 5))
2275 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002276 if (is_interface)
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002277 {
2278 emsg(_(e_final_variable_not_supported_in_interface));
2279 break;
2280 }
2281 has_final = TRUE;
2282 }
2283 else if (checkforcmd(&p, "const", 5))
2284 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002285 if (is_interface)
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002286 {
2287 emsg(_(e_const_variable_not_supported_in_interface));
2288 break;
2289 }
2290 has_const = TRUE;
2291 }
2292 p = skipwhite(p);
2293
Bram Moolenaard505d172022-12-18 21:42:55 +00002294 // object members (public, read access, private):
Doug Kearns74da0ee2023-12-14 20:26:26 +01002295 // "var _varname"
2296 // "var varname"
2297 // "public var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002298 // "final _varname"
2299 // "final varname"
2300 // "public final varname"
2301 // "const _varname"
2302 // "const varname"
2303 // "public const varname"
Doug Kearns74da0ee2023-12-14 20:26:26 +01002304 // class members (public, read access, private):
2305 // "static var _varname"
2306 // "static var varname"
2307 // "public static var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002308 // "static final _varname"
2309 // "static final varname"
2310 // "public static final varname"
2311 // "static const _varname"
2312 // "static const varname"
2313 // "public static const varname"
2314 if (has_var || has_final || has_const)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002315 {
Doug Kearns74da0ee2023-12-14 20:26:26 +01002316 char_u *varname = p;
Bram Moolenaard505d172022-12-18 21:42:55 +00002317 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00002318 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00002319 char_u *init_expr = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002320 int has_type = FALSE;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002321
Doug Kearns74da0ee2023-12-14 20:26:26 +01002322 if (!eval_isnamec1(*p))
2323 {
2324 if (has_static)
2325 semsg(_(e_invalid_class_variable_declaration_str), line);
2326 else
2327 semsg(_(e_invalid_object_variable_declaration_str), line);
2328 break;
2329 }
2330
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002331 if (is_interface && *varname == '_')
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002332 {
2333 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01002334 semsg(_(e_protected_variable_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002335 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002336 break;
2337 }
2338
Bram Moolenaard505d172022-12-18 21:42:55 +00002339 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002340 &varname_end, &has_type, &type_list, &type,
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002341 !is_interface ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002342 break;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002343
2344 if (is_reserved_varname(varname, varname_end)
2345 || is_duplicate_variable(&classmembers, &objmembers,
2346 varname, varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02002347 {
2348 vim_free(init_expr);
2349 break;
2350 }
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002351 if (add_member(has_static ? &classmembers : &objmembers, varname,
2352 varname_end, has_public, has_final, has_const,
2353 has_type, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00002354 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002355 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002356 break;
2357 }
Bram Moolenaard505d172022-12-18 21:42:55 +00002358 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002359
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002360 // constructors:
2361 // def new()
2362 // enddef
2363 // def newOther()
2364 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002365 // object methods and class functions:
2366 // def SomeMethod()
2367 // enddef
2368 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002369 // enddef
2370 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002371 // def <Tval> someMethod()
2372 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002373 else if (checkforcmd(&p, "def", 3))
2374 {
2375 exarg_T ea;
2376 garray_T lines_to_free;
Yegappan Lakshmanan7e898002025-02-11 22:07:05 +01002377 int is_new = STRNCMP(p, "new", 3) == 0
2378 || STRNCMP(p, "_new", 4) == 0;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002379
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002380 if (has_public)
2381 {
2382 // "public" keyword is not supported when defining an object or
2383 // class method
2384 emsg(_(e_public_keyword_not_supported_for_method));
2385 break;
2386 }
2387
2388 if (*p == NUL)
2389 {
2390 // No method name following def
2391 semsg(_(e_not_valid_command_in_class_str), line);
2392 break;
2393 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002394
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002395 if (is_interface && *p == '_')
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01002396 {
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002397 // private methods are not supported in an interface
2398 semsg(_(e_protected_method_not_supported_in_interface), p);
2399 break;
2400 }
2401
2402 if (has_static && !is_new && SAFE_islower(*p) &&
2403 is_valid_builtin_obj_methodname(p))
2404 {
2405 semsg(_(e_builtin_class_method_not_supported), p);
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01002406 break;
2407 }
2408
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002409 CLEAR_FIELD(ea);
2410 ea.cmd = line;
2411 ea.arg = p;
2412 ea.cmdidx = CMD_def;
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002413 ea.ea_getline = eap->ea_getline;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002414 ea.cookie = eap->cookie;
2415
2416 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002417 int class_flags;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002418 if (is_interface)
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002419 class_flags = CF_INTERFACE;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002420 else
2421 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
Bram Moolenaar554d0312023-01-05 19:59:18 +00002422 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02002423 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002424 ga_clear_strings(&lines_to_free);
2425
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002426 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002427 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002428 char_u *name = uf->uf_name;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02002429
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002430 if (is_new && !is_valid_constructor(uf, is_abstract,
2431 has_static))
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002432 {
2433 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01002434 semsg(_(e_protected_method_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002435 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002436 func_clear_free(uf, FALSE);
2437 break;
2438 }
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002439
2440 // check for builtin method
2441 if (!is_new && SAFE_islower(*name) &&
2442 !object_check_builtin_method_sig(uf))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00002443 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02002444 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00002445 break;
2446 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02002447
Bram Moolenaar58b40092023-01-11 15:59:05 +00002448 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002449 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002450 {
2451 success = FALSE;
2452 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02002453 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002454 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00002455
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002456 garray_T *fgap = has_static || is_new
2457 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002458 if (ga_grow(fgap, 1) == OK)
2459 {
2460 if (is_new)
2461 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002462
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002463 if (abstract_method)
2464 uf->uf_flags |= FC_ABSTRACT;
2465
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002466 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
2467 ++fgap->ga_len;
2468 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002469 }
2470 }
2471
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002472 else
2473 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00002474 if (is_class)
2475 semsg(_(e_not_valid_command_in_class_str), line);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002476 else if (is_enum)
2477 semsg(_(e_not_valid_command_in_enum_str), line);
Bram Moolenaar554d0312023-01-05 19:59:18 +00002478 else
2479 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002480 break;
2481 }
2482 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002483
2484 if (theline == NULL && !success && is_enum)
2485 emsg(_(e_missing_endenum));
2486
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002487 vim_free(theline);
2488
Yegappan Lakshmananabedca92024-03-29 10:08:23 +01002489 if (success && is_enum && num_enum_values == 0)
2490 // Empty enum statement. Add an empty "values" class variable
Hirohito Higashi5887cce2025-02-16 16:34:30 +01002491 success = enum_add_values_member(cl, &classmembers, 0, &type_list);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002492
Bram Moolenaar83677162023-01-08 19:54:10 +00002493 /*
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002494 * Check a few things
Bram Moolenaar83677162023-01-08 19:54:10 +00002495 */
2496
2497 // Check the "extends" class is valid.
2498 if (success && extends != NULL)
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002499 success = validate_extends_class(cl, extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00002500 VIM_CLEAR(extends);
2501
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002502 // Check the new object methods to make sure their access (public or
2503 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002504 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002505 success = validate_extends_methods(&objmethods, extends_cl);
2506
2507 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002508 // variables in the extended class lineage. If an interface is extending
2509 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002510 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002511 {
2512 if (is_class)
2513 success = extends_check_dup_members(&objmembers, extends_cl);
2514 else
2515 success = extends_check_intf_var_type(&objmembers, extends_cl);
2516 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002517
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002518 // When extending an abstract class, make sure all the abstract methods in
2519 // the parent class are implemented. If the current class is an abstract
2520 // class, then there is no need for this check.
2521 if (success && !is_abstract && extends_cl != NULL
2522 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002523 success = validate_abstract_class_methods(&classfunctions,
2524 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002525
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +01002526 // Process the "implements" entries
Bram Moolenaar83677162023-01-08 19:54:10 +00002527 // Check all "implements" entries are valid.
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +01002528 garray_T intf_classes_ga;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002529
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +01002530 ga_init2(&intf_classes_ga, sizeof(class_T *), 5);
2531
2532 if (success && ga_impl.ga_len > 0)
2533 success = validate_implements_classes(&ga_impl, &intf_classes_ga,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002534 &objmethods, &objmembers, extends_cl);
Yegappan Lakshmanan8e92db42025-01-13 07:30:11 +01002535
2536 // inherit the super class interfaces
2537 if (success && extends_cl != NULL)
2538 success = add_super_class_interfaces(extends_cl, &ga_impl,
2539 &intf_classes_ga);
2540
2541 intf_classes = intf_classes_ga.ga_data;
2542 intf_classes_ga.ga_len = 0;
Bram Moolenaar94674f22023-01-06 18:42:20 +00002543
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002544 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00002545 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002546 success = check_func_arg_names(&classfunctions, &objmethods,
2547 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00002548
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002549 if (success)
2550 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002551 // "endclass" or "endinterface" or "endenum" encountered without any
2552 // failures
Bram Moolenaard505d172022-12-18 21:42:55 +00002553
Bram Moolenaard0200c82023-01-28 15:19:40 +00002554 if (extends_cl != NULL)
2555 {
2556 cl->class_extends = extends_cl;
2557 extends_cl->class_flags |= CLASS_EXTENDED;
2558 }
Bram Moolenaar83677162023-01-08 19:54:10 +00002559
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002560 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00002561 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002562 NULL,
2563 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00002564 &cl->class_class_members,
2565 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00002566 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00002567 extends_cl == NULL ? NULL
2568 : extends_cl->class_obj_members,
2569 extends_cl == NULL ? 0
2570 : extends_cl->class_obj_member_count,
2571 &cl->class_obj_members,
2572 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002573 goto cleanup;
2574
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00002575 if (ga_impl.ga_len > 0)
2576 {
2577 // Move the "implements" names into the class.
2578 cl->class_interface_count = ga_impl.ga_len;
2579 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
2580 if (cl->class_interfaces == NULL)
2581 goto cleanup;
2582 for (int i = 0; i < ga_impl.ga_len; ++i)
2583 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
2584 VIM_CLEAR(ga_impl.ga_data);
2585 ga_impl.ga_len = 0;
2586
Bram Moolenaard0200c82023-01-28 15:19:40 +00002587 cl->class_interfaces_cl = intf_classes;
2588 intf_classes = NULL;
2589 }
2590
2591 if (cl->class_interface_count > 0 || extends_cl != NULL)
2592 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002593 // Add a method and member lookup table to each of the interface
2594 // classes.
2595 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
2596 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00002597 }
2598
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002599 int have_new = FALSE;
2600 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002601 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002602 {
2603 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
Yegappan Lakshmanan7e898002025-02-11 22:07:05 +01002604 if (STRCMP(class_func->uf_name, "new") == 0
2605 || STRCMP(class_func->uf_name, "_new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002606 {
2607 have_new = TRUE;
2608 break;
2609 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002610 }
2611
2612 if (have_new)
2613 // The return type of new() is an object of class "cl"
2614 class_func->uf_ret_type->tt_class = cl;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002615 else if ((is_class || is_enum) && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002616 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002617 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002618
Bram Moolenaar58b40092023-01-11 15:59:05 +00002619 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002620 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
2621 &objmethods) == FAIL)
2622 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002623
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002624 update_builtin_method_index(cl);
2625
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002626 class_created(cl);
2627
2628 // Allocate a typval for each class member and initialize it.
2629 if ((is_class || is_enum) && cl->class_class_member_count > 0)
2630 if (add_class_members(cl, eap, &type_list) == FAIL)
2631 goto cleanup;
2632
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002633 cl->class_type_list = type_list;
2634
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002635 if (is_enum)
2636 {
2637 // clear the constructor method names, so that an enum class cannot
2638 // be instantiated
2639 enum_clear_constructors(cl);
2640 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002641
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002642 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00002643 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002644
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002645 return;
2646 }
2647
2648cleanup:
Bram Moolenaar83677162023-01-08 19:54:10 +00002649 vim_free(extends);
2650 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002651
2652 if (intf_classes != NULL)
2653 {
2654 for (int i = 0; i < ga_impl.ga_len; ++i)
2655 class_unref(intf_classes[i]);
2656 vim_free(intf_classes);
2657 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00002658 ga_clear_strings(&ga_impl);
2659
Bram Moolenaard505d172022-12-18 21:42:55 +00002660 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002661 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002662 garray_T *gap = round == 1 ? &classmembers : &objmembers;
2663 if (gap->ga_len == 0 || gap->ga_data == NULL)
2664 continue;
2665
2666 for (int i = 0; i < gap->ga_len; ++i)
2667 {
2668 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2669 vim_free(m->ocm_name);
2670 vim_free(m->ocm_init);
2671 }
2672 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002673 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002674
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002675 for (int i = 0; i < objmethods.ga_len; ++i)
2676 {
2677 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2678 func_clear_free(uf, FALSE);
2679 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002680 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002681
2682 for (int i = 0; i < classfunctions.ga_len; ++i)
2683 {
2684 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2685 func_clear_free(uf, FALSE);
2686 }
2687 ga_clear(&classfunctions);
2688
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002689 clear_type_list(&type_list);
2690}
2691
2692/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002693 * Find member "name" in class "cl", set "member_idx" to the member index and
2694 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002695 * When "is_object" is TRUE, then look for object members. Otherwise look for
2696 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002697 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002698 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002699 */
2700 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002701oc_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002702 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002703 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002704 char_u *name,
2705 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002706 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002707{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002708 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002709 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002710
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002711 *member_idx = -1; // not found (yet)
2712
2713 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2714 member_idx);
2715 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002716 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002717 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2718 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002719 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002720 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002721
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002722 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002723}
2724
2725/*
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002726 * Given a class or object variable index, return the variable type
2727 */
2728 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002729oc_member_type_by_idx(
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002730 class_T *cl,
2731 int is_object,
2732 int member_idx)
2733{
2734 ocmember_T *m;
2735 int member_count;
2736
2737 if (is_object)
2738 {
2739 m = cl->class_obj_members;
2740 member_count = cl->class_obj_member_count;
2741 }
2742 else
2743 {
2744 m = cl->class_class_members;
2745 member_count = cl->class_class_member_count;
2746 }
2747
2748 if (member_idx >= member_count)
2749 return NULL;
2750
2751 return m[member_idx].ocm_type;
2752}
2753
2754/*
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002755 * Type aliases (:type)
2756 */
2757
Yegappan Lakshmanana04003a2024-10-27 21:54:11 +01002758 static void
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002759typealias_free(typealias_T *ta)
2760{
2761 // ta->ta_type is freed in clear_type_list()
2762 vim_free(ta->ta_name);
2763 vim_free(ta);
2764}
2765
2766 void
2767typealias_unref(typealias_T *ta)
2768{
2769 if (ta != NULL && --ta->ta_refcount <= 0)
2770 typealias_free(ta);
2771}
2772
2773/*
2774 * Handle ":type". Create an alias for a type specification.
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002775 */
2776 void
Dominique Pellé0268ff32024-07-28 21:12:20 +02002777ex_type(exarg_T *eap)
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002778{
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002779 char_u *arg = eap->arg;
2780
2781 if (!current_script_is_vim9()
2782 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002783 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002784 {
2785 emsg(_(e_type_can_only_be_defined_in_vim9_script));
2786 return;
2787 }
2788
2789 if (*arg == NUL)
2790 {
2791 emsg(_(e_missing_typealias_name));
2792 return;
2793 }
2794
2795 if (!ASCII_ISUPPER(*arg))
2796 {
2797 semsg(_(e_type_name_must_start_with_uppercase_letter_str), arg);
2798 return;
2799 }
2800
2801 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
2802 if (!IS_WHITE_OR_NUL(*name_end))
2803 {
2804 semsg(_(e_white_space_required_after_name_str), arg);
2805 return;
2806 }
2807 char_u *name_start = arg;
2808
2809 arg = skipwhite(name_end);
2810 if (*arg != '=')
2811 {
2812 semsg(_(e_missing_equal_str), arg);
2813 return;
2814 }
2815 if (!IS_WHITE_OR_NUL(*(arg + 1)))
2816 {
2817 semsg(_(e_white_space_required_after_str_str), "=", arg);
2818 return;
2819 }
2820 arg++;
2821 arg = skipwhite(arg);
2822
2823 if (*arg == NUL)
2824 {
2825 emsg(_(e_missing_typealias_type));
2826 return;
2827 }
2828
2829 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
2830 type_T *type = parse_type(&arg, &si->sn_type_list, TRUE);
2831 if (type == NULL)
2832 return;
2833
2834 if (*arg != NUL)
2835 {
2836 // some text after the type
2837 semsg(_(e_trailing_characters_str), arg);
2838 return;
2839 }
2840
2841 int cc = *name_end;
2842 *name_end = NUL;
2843
2844 typval_T tv;
2845 tv.v_type = VAR_UNKNOWN;
2846 if (eval_variable_import(name_start, &tv) == OK)
2847 {
2848 if (tv.v_type == VAR_TYPEALIAS)
2849 semsg(_(e_typealias_already_exists_for_str), name_start);
2850 else
2851 semsg(_(e_redefining_script_item_str), name_start);
2852 clear_tv(&tv);
2853 goto done;
2854 }
2855
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02002856 // Create a script-local variable for the type alias.
2857 if (type->tt_type != VAR_OBJECT)
2858 {
2859 tv.v_type = VAR_TYPEALIAS;
2860 tv.v_lock = 0;
2861 tv.vval.v_typealias = ALLOC_CLEAR_ONE(typealias_T);
2862 ++tv.vval.v_typealias->ta_refcount;
2863 tv.vval.v_typealias->ta_name = vim_strsave(name_start);
2864 tv.vval.v_typealias->ta_type = type;
2865 }
2866 else
2867 {
2868 // When creating a type alias for a class, use the class type itself to
2869 // create the type alias variable. This is needed to use the type
2870 // alias to invoke class methods (e.g. new()) and use class variables.
2871 tv.v_type = VAR_CLASS;
2872 tv.v_lock = 0;
2873 tv.vval.v_class = type->tt_class;
2874 ++tv.vval.v_class->class_refcount;
2875 }
Yegappan Lakshmanane3fed482025-02-20 22:20:54 +01002876 set_var_const(name_start, current_sctx.sc_sid, NULL, &tv, FALSE,
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002877 ASSIGN_CONST | ASSIGN_FINAL, 0);
2878
2879done:
2880 *name_end = cc;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002881}
2882
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002883/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002884 * Returns OK if a member variable named "name" is present in the class "cl".
2885 * Otherwise returns FAIL. If found, the member variable typval is set in
2886 * "rettv". If "is_object" is TRUE, then the object member variable table is
2887 * searched. Otherwise the class member variable table is searched.
2888 */
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002889 int
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002890get_member_tv(
2891 class_T *cl,
2892 int is_object,
2893 char_u *name,
2894 size_t namelen,
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002895 class_T *current_class,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002896 typval_T *rettv)
2897{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002898 ocmember_T *m;
2899 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002900
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002901 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2902 &m_idx);
2903 if (m == NULL)
2904 return FAIL;
2905
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002906 if (*name == '_' && (current_class == NULL ||
2907 !class_instance_of(current_class, cl)))
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002908 {
Ernie Rael03042a22023-11-11 08:53:32 +01002909 emsg_var_cl_define(e_cannot_access_protected_variable_str,
Ernie Raele6c9aa52023-10-06 19:55:52 +02002910 m->ocm_name, 0, cl);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002911 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002912 }
2913
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002914 if (is_object)
2915 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002916 // The object only contains a pointer to the class, the member values
2917 // array follows right after that.
2918 object_T *obj = rettv->vval.v_object;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002919 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2920 copy_tv(tv, rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002921 object_unref(obj);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002922 }
2923 else
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002924 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002925 copy_tv(&cl->class_members_tv[m_idx], rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002926 class_unref(cl);
2927 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002928
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002929 return OK;
2930}
2931
2932/*
2933 * Call an object or class method "name" in class "cl". The method return
2934 * value is returned in "rettv".
2935 */
2936 static int
2937call_oc_method(
2938 class_T *cl,
2939 char_u *name,
2940 size_t len,
2941 char_u *name_end,
2942 evalarg_T *evalarg,
2943 char_u **arg,
2944 typval_T *rettv)
2945{
2946 ufunc_T *fp;
2947 typval_T argvars[MAX_FUNC_ARGS + 1];
2948 int argcount = 0;
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002949 ocmember_T *ocm = NULL;
2950 int m_idx;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002951
2952 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
2953 if (fp == NULL)
2954 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002955 // could be an object or class funcref variable
2956 ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
2957 if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
2958 {
2959 method_not_found_msg(cl, rettv->v_type, name, len);
2960 return FAIL;
2961 }
2962
Yegappan Lakshmanan3e336502024-04-04 19:35:59 +02002963 if (*name == '_')
2964 {
2965 // Protected object or class funcref variable
2966 semsg(_(e_cannot_access_protected_variable_str), ocm->ocm_name,
2967 cl->class_name);
2968 return FAIL;
2969 }
2970
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002971 if (rettv->v_type == VAR_OBJECT)
2972 {
2973 // funcref object variable
2974 object_T *obj = rettv->vval.v_object;
2975 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2976 copy_tv(tv, rettv);
2977 }
2978 else
2979 // funcref class variable
2980 copy_tv(&cl->class_members_tv[m_idx], rettv);
2981 *arg = name_end;
2982 return OK;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002983 }
2984
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002985 if (ocm == NULL && *fp->uf_name == '_')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002986 {
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002987 // Cannot access a protected method outside of a class
Ernie Rael03042a22023-11-11 08:53:32 +01002988 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002989 return FAIL;
2990 }
2991
2992 char_u *argp = name_end;
Ernie Raelb077b582023-12-14 20:11:44 +01002993 int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount, FALSE);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002994 if (ret == FAIL)
2995 return FAIL;
2996
2997 funcexe_T funcexe;
2998 CLEAR_FIELD(funcexe);
2999 funcexe.fe_evaluate = TRUE;
3000 if (rettv->v_type == VAR_OBJECT)
3001 {
3002 funcexe.fe_object = rettv->vval.v_object;
3003 ++funcexe.fe_object->obj_refcount;
3004 }
3005
3006 // Clear the class or object after calling the function, in
3007 // case the refcount is one.
3008 typval_T tv_tofree = *rettv;
3009 rettv->v_type = VAR_UNKNOWN;
3010
3011 // Call the user function. Result goes into rettv;
3012 int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
3013 NULL);
3014
3015 // Clear the previous rettv and the arguments.
3016 clear_tv(&tv_tofree);
3017 for (int idx = 0; idx < argcount; ++idx)
3018 clear_tv(&argvars[idx]);
3019
3020 if (error != FCERR_NONE)
3021 {
3022 user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
3023 return FAIL;
3024 }
3025 *arg = argp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003026
3027 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003028}
3029
3030/*
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003031 * Create a partial typval for "obj.obj_method" and store it in "rettv".
3032 * Returns OK on success and FAIL on memory allocation failure.
3033 */
3034 int
3035obj_method_to_partial_tv(object_T *obj, ufunc_T *obj_method, typval_T *rettv)
3036{
3037 partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
3038 if (pt == NULL)
3039 return FAIL;
3040
3041 pt->pt_refcount = 1;
3042 if (obj != NULL)
3043 {
3044 pt->pt_obj = obj;
3045 ++pt->pt_obj->obj_refcount;
3046 }
3047 pt->pt_auto = TRUE;
3048 pt->pt_func = obj_method;
3049 func_ptr_ref(pt->pt_func);
3050
3051 rettv->v_type = VAR_PARTIAL;
3052 rettv->vval.v_partial = pt;
3053
3054 return OK;
3055}
3056
3057/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003058 * Evaluate what comes after a class:
3059 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003060 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003061 * - class constructor: SomeClass.new()
3062 * - object member: someObject.varname
3063 * - object method: someObject.SomeMethod()
3064 *
3065 * "*arg" points to the '.'.
3066 * "*arg" is advanced to after the member name or method call.
3067 *
3068 * Returns FAIL or OK.
3069 */
3070 int
3071class_object_index(
3072 char_u **arg,
3073 typval_T *rettv,
3074 evalarg_T *evalarg,
3075 int verbose UNUSED) // give error messages
3076{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003077 if (VIM_ISWHITE((*arg)[1]))
3078 {
3079 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
3080 return FAIL;
3081 }
3082
3083 ++*arg;
3084 char_u *name = *arg;
3085 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
3086 if (name_end == name)
3087 return FAIL;
3088 size_t len = name_end - name;
3089
Ernie Raeld615a312023-10-05 20:28:16 +02003090 int did_emsg_save = did_emsg;
Bram Moolenaar552bdca2023-02-17 21:08:50 +00003091 class_T *cl;
3092 if (rettv->v_type == VAR_CLASS)
3093 cl = rettv->vval.v_class;
3094 else // VAR_OBJECT
3095 {
3096 if (rettv->vval.v_object == NULL)
3097 {
3098 emsg(_(e_using_null_object));
3099 return FAIL;
3100 }
3101 cl = rettv->vval.v_object->obj_class;
3102 }
3103
Bram Moolenaard13dd302023-03-11 20:56:35 +00003104 if (cl == NULL)
3105 {
3106 emsg(_(e_incomplete_type));
3107 return FAIL;
3108 }
3109
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003110 if (*name_end == '(')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003111 // Invoke the class or object method
3112 return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003113
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003114 else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003115 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003116 // Search in the object member variable table and the class member
3117 // variable table.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003118 int is_object = rettv->v_type == VAR_OBJECT;
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003119 if (get_member_tv(cl, is_object, name, len, NULL, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003120 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003121 *arg = name_end;
3122 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003123 }
3124
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003125 // could be a class method or an object method
3126 int fidx;
3127 ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
3128 if (fp != NULL)
3129 {
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003130 // Protected methods are not accessible outside the class
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003131 if (*name == '_')
3132 {
Ernie Rael03042a22023-11-11 08:53:32 +01003133 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003134 return FAIL;
3135 }
3136
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003137 if (obj_method_to_partial_tv(is_object ? rettv->vval.v_object :
3138 NULL, fp, rettv) == FAIL)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003139 return FAIL;
3140
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003141 *arg = name_end;
3142 return OK;
3143 }
3144
Ernie Raeld615a312023-10-05 20:28:16 +02003145 if (did_emsg == did_emsg_save)
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +02003146 member_not_found_msg(cl, rettv->v_type, name, len);
Bram Moolenaard505d172022-12-18 21:42:55 +00003147 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003148
3149 return FAIL;
3150}
3151
3152/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003153 * If "arg" points to a class or object method, return it.
3154 * Otherwise return NULL.
3155 */
3156 ufunc_T *
3157find_class_func(char_u **arg)
3158{
3159 char_u *name = *arg;
3160 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
3161 if (name_end == name || *name_end != '.')
3162 return NULL;
3163
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003164 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003165 size_t len = name_end - name;
3166 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003167 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00003168 if (eval_variable(name, (int)len,
3169 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003170 return NULL;
3171 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00003172 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003173
3174 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
3175 : tv.vval.v_object->obj_class;
3176 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00003177 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003178 char_u *fname = name_end + 1;
3179 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
3180 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00003181 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003182 len = fname_end - fname;
3183
Ernie Rael4d00b832023-09-11 19:54:42 +02003184 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003185
Bram Moolenaareb533502022-12-14 15:06:11 +00003186fail_after_eval:
3187 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003188 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003189}
3190
3191/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003192 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003193 * Returns -1, if the variable is not found.
3194 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003195 */
3196 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003197class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003198{
Ernie Rael4d00b832023-09-11 19:54:42 +02003199 int idx;
3200 class_member_lookup(cl, name, namelen, &idx);
3201 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003202}
3203
3204/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003205 * Returns a pointer to the class member variable "name" in the class "cl".
3206 * Returns NULL if the variable is not found.
3207 * The member variable index is set in "idx".
3208 */
3209 ocmember_T *
3210class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3211{
Ernie Rael4d00b832023-09-11 19:54:42 +02003212 ocmember_T *ret_m = NULL;
3213 int ret_idx = -1;
3214 for (int i = 0; i < cl->class_class_member_count; ++i)
3215 {
3216 ocmember_T *m = &cl->class_class_members[i];
3217 if (namelen)
3218 {
3219 if (STRNCMP(name, m->ocm_name, namelen) == 0
3220 && m->ocm_name[namelen] == NUL)
3221 {
3222 ret_m = m;
3223 ret_idx = i;
3224 break;
3225 }
3226 }
3227 else if (STRCMP(name, m->ocm_name) == 0)
3228 {
3229 ret_m = m;
3230 ret_idx = i;
zeertzjqd9be94c2024-07-14 10:20:20 +02003231 break;
Ernie Rael4d00b832023-09-11 19:54:42 +02003232 }
3233 }
3234 if (idx != NULL)
3235 *idx = ret_idx;
3236 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003237}
3238
3239/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003240 * Returns a pointer to the class method "name" in class "cl".
3241 * Returns NULL if the method is not found.
3242 * The method index is set in "idx".
3243 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003244 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003245class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3246{
Ernie Rael4d00b832023-09-11 19:54:42 +02003247 ufunc_T *ret_fp = NULL;
3248 int ret_idx = -1;
3249 for (int i = 0; i < cl->class_class_function_count; ++i)
3250 {
3251 ufunc_T *fp = cl->class_class_functions[i];
3252 char_u *ufname = (char_u *)fp->uf_name;
3253 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
3254 {
3255 ret_fp = fp;
3256 ret_idx = i;
3257 break;
3258 }
3259 }
3260 if (idx != NULL)
3261 *idx = ret_idx;
3262 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003263}
3264
3265/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003266 * Returns the index of class method "name" in the class "cl".
3267 * Returns -1, if the method is not found.
3268 */
3269 int
3270class_method_idx(class_T *cl, char_u *name, size_t namelen)
3271{
3272 int idx;
3273 class_method_lookup(cl, name, namelen, &idx);
3274 return idx;
3275}
3276
3277/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003278 * Returns the index of object member variable "name" in the class "cl".
3279 * Returns -1, if the variable is not found.
3280 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
3281 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003282 static int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003283object_member_idx(class_T *cl, char_u *name, size_t namelen)
3284{
Ernie Rael4d00b832023-09-11 19:54:42 +02003285 int idx;
3286 object_member_lookup(cl, name, namelen, &idx);
3287 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02003288}
3289
3290/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003291 * Returns a pointer to the object member variable "name" in the class "cl".
3292 * Returns NULL if the variable is not found.
3293 * The object member variable index is set in "idx".
3294 */
3295 ocmember_T *
3296object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3297{
Ernie Rael4d00b832023-09-11 19:54:42 +02003298 ocmember_T *ret_m = NULL;
3299 int ret_idx = -1;
3300 for (int i = 0; i < cl->class_obj_member_count; ++i)
3301 {
3302 ocmember_T *m = &cl->class_obj_members[i];
3303 if (namelen)
3304 {
3305 if (STRNCMP(name, m->ocm_name, namelen) == 0
3306 && m->ocm_name[namelen] == NUL)
3307 {
3308 ret_m = m;
3309 ret_idx = i;
3310 break;
3311 }
3312 }
3313 else if (STRCMP(name, m->ocm_name) == 0)
zeertzjqd9be94c2024-07-14 10:20:20 +02003314 {
Ernie Rael4d00b832023-09-11 19:54:42 +02003315 ret_m = m;
3316 ret_idx = i;
zeertzjqd9be94c2024-07-14 10:20:20 +02003317 break;
3318 }
Ernie Rael4d00b832023-09-11 19:54:42 +02003319 }
3320 if (idx != NULL)
3321 *idx = ret_idx;
3322 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003323}
3324
3325/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003326 * Returns a pointer to the object method "name" in class "cl".
3327 * Returns NULL if the method is not found.
3328 * The object method index is set in "idx".
3329 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003330 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003331object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3332{
Ernie Rael4d00b832023-09-11 19:54:42 +02003333 ufunc_T *ret_fp = NULL;
3334 int ret_idx = -1;
3335 for (int i = 0; i < cl->class_obj_method_count; ++i)
3336 {
3337 ufunc_T *fp = cl->class_obj_methods[i];
3338 // Use a separate pointer to avoid that ASAN complains about
3339 // uf_name[] only being 4 characters.
3340 char_u *ufname = (char_u *)fp->uf_name;
3341 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
3342 {
3343 ret_fp = fp;
3344 ret_idx = i;
3345 break;
3346 }
3347 }
3348 if (idx != NULL)
3349 *idx = ret_idx;
3350 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003351}
3352
3353/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003354 * Returns the index of object method "name" in the class "cl".
3355 * Returns -1, if the method is not found.
3356 */
3357 int
3358object_method_idx(class_T *cl, char_u *name, size_t namelen)
3359{
3360 int idx;
3361 object_method_lookup(cl, name, namelen, &idx);
3362 return idx;
3363}
3364
3365/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003366 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
3367 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
3368 * object member variable.
3369 *
3370 * Returns a pointer to the member variable structure if variable is found.
3371 * Otherwise returns NULL. The member variable index is set in "*idx".
3372 */
3373 ocmember_T *
3374member_lookup(
3375 class_T *cl,
3376 vartype_T v_type,
3377 char_u *name,
3378 size_t namelen,
3379 int *idx)
3380{
3381 if (v_type == VAR_CLASS)
3382 return class_member_lookup(cl, name, namelen, idx);
3383 else
3384 return object_member_lookup(cl, name, namelen, idx);
3385}
3386
3387/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003388 * Find the class that defines the named member. Look up the hierarchy
3389 * starting at "cl".
3390 *
3391 * Return the class that defines the member "name", else NULL.
3392 * Fill in "p_m", if specified, for ocmember_T in found class.
3393 */
3394// NOTE: if useful for something could also indirectly return vartype and idx.
3395 static class_T *
3396class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m)
3397{
3398 class_T *cl_found = NULL;
3399 vartype_T vartype = VAR_UNKNOWN;
3400 ocmember_T *m_found = NULL;
3401
3402 len = len != 0 ? len : STRLEN(name);
3403
3404 // Loop assumes if member is not defined in "cl", then it is not
3405 // defined in any super class; the last class where it's found is the
3406 // class where it is defined. Once the vartype is found, the other
3407 // type is no longer checked.
3408 for (class_T *super = cl; super != NULL; super = super->class_extends)
3409 {
3410 class_T *cl_tmp = NULL;
3411 ocmember_T *m = NULL;
3412 if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT)
3413 {
3414 if ((m = object_member_lookup(super, name, len, NULL)) != NULL)
3415 {
3416 cl_tmp = super;
3417 vartype = VAR_OBJECT;
3418 }
3419 }
3420 if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS)
3421 {
3422 if (( m = class_member_lookup(super, name, len, NULL)) != NULL)
3423 {
3424 cl_tmp = super;
Zdenek Dohnal215c82d2024-12-04 20:19:40 +01003425 vartype = VAR_CLASS;
Ernie Raele6c9aa52023-10-06 19:55:52 +02003426 }
3427 }
3428 if (cl_tmp == NULL)
3429 break; // member is not in this or any super class.
3430 cl_found = cl_tmp;
3431 m_found = m;
3432 }
3433 if (p_m != NULL)
3434 *p_m = m_found;
3435 return cl_found;
3436}
3437
3438/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003439 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
3440 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
3441 *
3442 * Returns a pointer to the method structure if variable is found.
3443 * Otherwise returns NULL. The method variable index is set in "*idx".
3444 */
3445 ufunc_T *
3446method_lookup(
3447 class_T *cl,
3448 vartype_T v_type,
3449 char_u *name,
3450 size_t namelen,
3451 int *idx)
3452{
3453 if (v_type == VAR_CLASS)
3454 return class_method_lookup(cl, name, namelen, idx);
3455 else
3456 return object_method_lookup(cl, name, namelen, idx);
3457}
3458
3459/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00003460 * Return TRUE if current context "cctx_arg" is inside class "cl".
3461 * Return FALSE if not.
3462 */
3463 int
3464inside_class(cctx_T *cctx_arg, class_T *cl)
3465{
3466 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02003467 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003468 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00003469 return TRUE;
3470 return FALSE;
3471}
3472
3473/*
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01003474 * Return TRUE if object/class variable "m" is read-only.
3475 * Also give an error message.
3476 */
3477 int
3478oc_var_check_ro(class_T *cl, ocmember_T *m)
3479{
3480 if (m->ocm_flags & (OCMFLAG_FINAL | OCMFLAG_CONST))
3481 {
3482 semsg(_(e_cannot_change_readonly_variable_str_in_class_str),
3483 m->ocm_name, cl->class_name);
3484 return TRUE;
3485 }
3486 return FALSE;
3487}
3488
3489/*
3490 * Lock all the constant object variables. Called after creating and
3491 * initializing a new object.
3492 */
3493 void
3494obj_lock_const_vars(object_T *obj)
3495{
3496 for (int i = 0; i < obj->obj_class->class_obj_member_count; i++)
3497 {
3498 ocmember_T *ocm = &obj->obj_class->class_obj_members[i];
3499 if (ocm->ocm_flags & OCMFLAG_CONST)
3500 {
3501 typval_T *mtv = ((typval_T *)(obj + 1)) + i;
3502 item_lock(mtv, DICT_MAXNEST, TRUE, TRUE);
3503 }
3504 }
3505}
3506
3507/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003508 * Make a copy of an object.
3509 */
3510 void
3511copy_object(typval_T *from, typval_T *to)
3512{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003513 if (from->vval.v_object == NULL)
3514 to->vval.v_object = NULL;
3515 else
3516 {
3517 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003518 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003519 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003520}
3521
3522/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003523 * Make a copy of a class.
3524 */
3525 void
3526copy_class(typval_T *from, typval_T *to)
3527{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003528 if (from->vval.v_class == NULL)
3529 to->vval.v_class = NULL;
3530 else
3531 {
3532 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003533 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003534 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003535}
3536
3537/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003538 * Free the class "cl" and its contents.
3539 */
3540 static void
3541class_free(class_T *cl)
3542{
3543 // Freeing what the class contains may recursively come back here.
3544 // Clear "class_name" first, if it is NULL the class does not need to
3545 // be freed.
3546 VIM_CLEAR(cl->class_name);
3547
3548 class_unref(cl->class_extends);
3549
3550 for (int i = 0; i < cl->class_interface_count; ++i)
3551 {
3552 vim_free(((char_u **)cl->class_interfaces)[i]);
3553 if (cl->class_interfaces_cl[i] != NULL)
3554 class_unref(cl->class_interfaces_cl[i]);
3555 }
3556 vim_free(cl->class_interfaces);
3557 vim_free(cl->class_interfaces_cl);
3558
3559 itf2class_T *next;
3560 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
3561 {
3562 next = i2c->i2c_next;
3563 vim_free(i2c);
3564 }
3565
3566 for (int i = 0; i < cl->class_class_member_count; ++i)
3567 {
3568 ocmember_T *m = &cl->class_class_members[i];
3569 vim_free(m->ocm_name);
3570 vim_free(m->ocm_init);
3571 if (cl->class_members_tv != NULL)
3572 clear_tv(&cl->class_members_tv[i]);
3573 }
3574 vim_free(cl->class_class_members);
3575 vim_free(cl->class_members_tv);
3576
3577 for (int i = 0; i < cl->class_obj_member_count; ++i)
3578 {
3579 ocmember_T *m = &cl->class_obj_members[i];
3580 vim_free(m->ocm_name);
3581 vim_free(m->ocm_init);
3582 }
3583 vim_free(cl->class_obj_members);
3584
3585 for (int i = 0; i < cl->class_class_function_count; ++i)
3586 {
3587 ufunc_T *uf = cl->class_class_functions[i];
3588 func_clear_free(uf, FALSE);
3589 }
3590 vim_free(cl->class_class_functions);
3591
3592 for (int i = 0; i < cl->class_obj_method_count; ++i)
3593 {
3594 ufunc_T *uf = cl->class_obj_methods[i];
3595 func_clear_free(uf, FALSE);
3596 }
3597 vim_free(cl->class_obj_methods);
3598
3599 clear_type_list(&cl->class_type_list);
3600
3601 class_cleared(cl);
3602
3603 vim_free(cl);
3604}
3605
3606/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003607 * Unreference a class. Free it when the reference count goes down to zero.
3608 */
3609 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003610class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003611{
Bram Moolenaard505d172022-12-18 21:42:55 +00003612 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003613 class_free(cl);
3614}
3615
3616/*
3617 * Go through the list of all classes and free items without "copyID".
3618 */
3619 int
3620class_free_nonref(int copyID)
3621{
3622 int did_free = FALSE;
3623
3624 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003625 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003626 next_nonref_class = cl->class_next_used;
3627 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003628 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003629 // Free the class and items it contains.
3630 class_free(cl);
3631 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003632 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003633 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003634
3635 next_nonref_class = NULL;
3636 return did_free;
3637}
3638
3639 int
3640set_ref_in_classes(int copyID)
3641{
3642 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3643 set_ref_in_item_class(cl, copyID, NULL, NULL);
3644
3645 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003646}
3647
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003648static object_T *first_object = NULL;
3649
3650/*
3651 * Call this function when an object has been created. It will be added to the
3652 * list headed by "first_object".
3653 */
3654 void
3655object_created(object_T *obj)
3656{
3657 if (first_object != NULL)
3658 {
3659 obj->obj_next_used = first_object;
3660 first_object->obj_prev_used = obj;
3661 }
3662 first_object = obj;
3663}
3664
3665/*
3666 * Call this function when an object has been cleared and is about to be freed.
3667 * It is removed from the list headed by "first_object".
3668 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003669 static void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003670object_cleared(object_T *obj)
3671{
3672 if (obj->obj_next_used != NULL)
3673 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
3674 if (obj->obj_prev_used != NULL)
3675 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
3676 else if (first_object == obj)
3677 first_object = obj->obj_next_used;
3678}
3679
3680/*
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003681 * Free the contents of an object ignoring the reference count.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003682 */
3683 static void
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003684object_free_contents(object_T *obj)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003685{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003686 class_T *cl = obj->obj_class;
3687
3688 if (!cl)
3689 return;
3690
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003691 // Avoid a recursive call, it can happen if "obj" has a circular reference.
3692 obj->obj_refcount = INT_MAX;
3693
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003694 // the member values are just after the object structure
3695 typval_T *tv = (typval_T *)(obj + 1);
3696 for (int i = 0; i < cl->class_obj_member_count; ++i)
3697 clear_tv(tv + i);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003698}
3699
3700 static void
3701object_free_object(object_T *obj)
3702{
3703 class_T *cl = obj->obj_class;
3704
3705 if (!cl)
3706 return;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003707
3708 // Remove from the list headed by "first_object".
3709 object_cleared(obj);
3710
3711 vim_free(obj);
3712 class_unref(cl);
3713}
3714
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003715 static void
3716object_free(object_T *obj)
3717{
3718 if (in_free_unref_items)
3719 return;
3720
3721 object_free_contents(obj);
3722 object_free_object(obj);
3723}
3724
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003725/*
3726 * Unreference an object.
3727 */
3728 void
3729object_unref(object_T *obj)
3730{
3731 if (obj != NULL && --obj->obj_refcount <= 0)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003732 object_free(obj);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003733}
3734
3735/*
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003736 * Go through the list of all objects and free items without "copyID".
3737 */
3738 int
3739object_free_nonref(int copyID)
3740{
3741 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003742
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003743 for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003744 {
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003745 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3746 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003747 // Free the object contents. Object itself will be freed later.
3748 object_free_contents(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003749 did_free = TRUE;
3750 }
3751 }
3752
3753 return did_free;
3754}
3755
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003756 void
3757object_free_items(int copyID)
3758{
3759 object_T *obj_next;
3760
3761 for (object_T *obj = first_object; obj != NULL; obj = obj_next)
3762 {
3763 obj_next = obj->obj_next_used;
3764 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3765 object_free_object(obj);
3766 }
3767}
3768
LemonBoyafe04662023-08-23 21:08:11 +02003769/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003770 * Output message which takes a variable name and the class that defines it.
3771 * "cl" is that class where the name was found. Search "cl"'s hierarchy to
3772 * find the defining class.
3773 */
3774 void
3775emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl)
3776{
3777 ocmember_T *m;
3778 class_T *cl_def = class_defining_member(cl, name, len, &m);
3779 if (cl_def != NULL)
3780 semsg(_(msg), m->ocm_name, cl_def->class_name);
3781 else
3782 emsg(_(e_internal_error_please_report_a_bug));
3783}
3784
3785/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003786 * Echo a class or object method not found message.
3787 */
3788 void
3789method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3790{
3791 char_u *method_name = vim_strnsave(name, len);
3792 if ((v_type == VAR_OBJECT)
3793 && (class_method_idx(cl, name, len) >= 0))
3794 {
3795 // If this is a class method, then give a different error
3796 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003797 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003798 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003799 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003800 method_name, cl->class_name);
3801 }
3802 else if ((v_type == VAR_CLASS)
3803 && (object_method_idx(cl, name, len) >= 0))
3804 {
3805 // If this is an object 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_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003810 method_name, cl->class_name);
3811 }
3812 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003813 semsg(_(e_method_not_found_on_class_str_str), method_name,
zeertzjqd9be94c2024-07-14 10:20:20 +02003814 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003815 vim_free(method_name);
3816}
3817
3818/*
3819 * Echo a class or object member not found message.
3820 */
3821 void
3822member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3823{
3824 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
3825
3826 if (v_type == VAR_OBJECT)
3827 {
3828 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003829 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003830 varname, cl->class_name);
3831 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003832 semsg(_(e_variable_not_found_on_object_str_str), varname,
3833 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003834 }
3835 else
3836 {
3837 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003838 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003839 varname, cl->class_name);
3840 else
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01003841 {
3842 if (IS_ENUM(cl))
3843 semsg(_(e_enum_value_str_not_found_in_enum_str),
3844 varname, cl->class_name);
3845 else
3846 semsg(_(e_class_variable_str_not_found_in_class_str),
3847 varname, cl->class_name);
3848 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003849 }
3850 vim_free(varname);
3851}
3852
3853/*
Yegappan Lakshmanan4f32c832024-01-12 17:36:40 +01003854 * Compile all the class and object methods in "cl".
3855 */
3856 void
3857defcompile_class(class_T *cl)
3858{
3859 for (int loop = 1; loop <= 2; ++loop)
3860 {
3861 int func_count = loop == 1 ? cl->class_class_function_count
3862 : cl->class_obj_method_count;
3863 for (int i = 0; i < func_count; i++)
3864 {
3865 ufunc_T *ufunc = loop == 1 ? cl->class_class_functions[i]
3866 : cl->class_obj_methods[i];
Yegappan Lakshmanan1af0fbf2024-04-09 21:39:27 +02003867 // Don't compile abstract methods
3868 if (!IS_ABSTRACT_METHOD(ufunc))
3869 defcompile_function(ufunc, cl);
Yegappan Lakshmanan4f32c832024-01-12 17:36:40 +01003870 }
3871 }
3872}
3873
3874/*
3875 * Compile all the classes defined in the current script
3876 */
3877 void
3878defcompile_classes_in_script(void)
3879{
3880 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3881 {
3882 if (eval_variable(cl->class_name, 0, 0, NULL, NULL,
3883 EVAL_VAR_NOAUTOLOAD | EVAL_VAR_NO_FUNC) != FAIL)
3884 defcompile_class(cl);
3885 }
3886}
3887
3888/*
3889 * Returns TRUE if "name" is the name of a class. The typval for the class is
3890 * returned in "rettv".
3891 */
3892 int
3893is_class_name(char_u *name, typval_T *rettv)
3894{
3895 rettv->v_type = VAR_UNKNOWN;
3896
3897 if (eval_variable(name, 0, 0, rettv, NULL, EVAL_VAR_NOAUTOLOAD |
3898 EVAL_VAR_NO_FUNC) != FAIL)
3899 return rettv->v_type == VAR_CLASS;
3900 return FALSE;
3901}
3902
3903/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003904 * Calls the object builtin method "name" with arguments "argv". The value
3905 * returned by the builtin method is in "rettv". Returns OK or FAIL.
3906 */
3907 static int
3908object_call_builtin_method(
3909 object_T *obj,
3910 class_builtin_T builtin_method,
3911 int argc,
3912 typval_T *argv,
3913 typval_T *rettv)
3914{
3915 ufunc_T *uf;
3916 int midx;
3917
3918 if (obj == NULL)
3919 return FAIL;
3920
3921 uf = class_get_builtin_method(obj->obj_class, builtin_method, &midx);
3922 if (uf == NULL)
3923 return FAIL;
3924
3925 funccall_T *fc = create_funccal(uf, rettv);
3926 int r;
3927
3928 if (fc == NULL)
3929 return FAIL;
3930
3931 ++obj->obj_refcount;
3932
3933 r = call_def_function(uf, argc, argv, 0, NULL, obj, fc, rettv);
3934
3935 remove_funccal();
3936
3937 return r;
3938}
3939
3940/*
zeertzjqc029c132024-03-28 11:37:26 +01003941 * Calls the object "empty()" method and returns the method return value. In
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003942 * case of an error, returns TRUE.
3943 */
3944 int
3945object_empty(object_T *obj)
3946{
3947 typval_T rettv;
3948
3949 if (object_call_builtin_method(obj, CLASS_BUILTIN_EMPTY, 0, NULL, &rettv)
3950 == FAIL)
3951 return TRUE;
3952
3953 return tv_get_bool(&rettv);
3954}
3955
3956/*
3957 * Use the object "len()" method to get an object length. Returns 0 if the
3958 * method is not found or there is an error.
3959 */
3960 int
3961object_len(object_T *obj)
3962{
3963 typval_T rettv;
3964
3965 if (object_call_builtin_method(obj, CLASS_BUILTIN_LEN, 0, NULL, &rettv)
3966 == FAIL)
3967 return 0;
3968
3969 return tv_to_number(&rettv);
3970}
3971
3972/*
LemonBoy7b29cc92024-06-22 17:25:07 +02003973 * Return TRUE when two objects have exactly the same values.
3974 */
3975 int
3976object_equal(
3977 object_T *o1,
3978 object_T *o2,
Yinzuo Jiang7ccd1a22024-07-04 17:20:53 +02003979 int ic) // ignore case for strings
LemonBoy7b29cc92024-06-22 17:25:07 +02003980{
3981 class_T *cl1, *cl2;
3982
3983 if (o1 == o2)
3984 return TRUE;
Ernie Rael86257142024-06-23 09:54:45 +02003985 if (o1 == NULL || o2 == NULL)
3986 return FALSE;
LemonBoy7b29cc92024-06-22 17:25:07 +02003987
3988 cl1 = o1->obj_class;
3989 cl2 = o2->obj_class;
3990
3991 if (cl1 != cl2 || cl1 == NULL || cl2 == NULL)
3992 return FALSE;
3993
3994 for (int i = 0; i < cl1->class_obj_member_count; ++i)
Yinzuo Jiang7ccd1a22024-07-04 17:20:53 +02003995 if (!tv_equal((typval_T *)(o1 + 1) + i, (typval_T *)(o2 + 1) + i, ic))
LemonBoy7b29cc92024-06-22 17:25:07 +02003996 return FALSE;
3997
3998 return TRUE;
3999}
4000
4001/*
Ernie Rael05ff4e42024-07-04 16:50:11 +02004002 * Return a textual representation of object "obj".
4003 * "obj" must not be NULL.
4004 * May return NULL.
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004005 */
4006 char_u *
Yegappan Lakshmanan22029ed2024-05-20 13:57:11 +02004007object2string(
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004008 object_T *obj,
4009 char_u *numbuf,
4010 int copyID,
4011 int echo_style,
4012 int restore_copyID,
4013 int composite_val)
4014{
4015 typval_T rettv;
4016
4017 if (object_call_builtin_method(obj, CLASS_BUILTIN_STRING, 0, NULL, &rettv)
4018 == OK
4019 && rettv.vval.v_string != NULL)
4020 return rettv.vval.v_string;
Ernie Rael05ff4e42024-07-04 16:50:11 +02004021
4022 int ok = OK;
4023 class_T *cl = obj->obj_class;
4024 garray_T ga;
4025 ga_init2(&ga, 1, 50);
4026
4027 if (cl != NULL && IS_ENUM(cl))
4028 {
4029 ga_concat(&ga, (char_u *)"enum ");
4030 ga_concat(&ga, cl->class_name);
4031 char_u *enum_name = ((typval_T *)(obj + 1))->vval.v_string;
4032 ga_concat(&ga, (char_u *)".");
4033 ga_concat(&ga, enum_name);
4034 }
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004035 else
4036 {
Ernie Rael05ff4e42024-07-04 16:50:11 +02004037 ga_concat(&ga, (char_u *)"object of ");
4038 ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]"
4039 : cl->class_name);
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004040 }
Ernie Rael05ff4e42024-07-04 16:50:11 +02004041 if (cl != NULL)
4042 {
4043 ga_concat(&ga, (char_u *)" {");
4044 for (int i = 0; i < cl->class_obj_member_count; ++i)
4045 {
4046 if (i > 0)
4047 ga_concat(&ga, (char_u *)", ");
4048 ocmember_T *m = &cl->class_obj_members[i];
4049 ga_concat(&ga, m->ocm_name);
4050 ga_concat(&ga, (char_u *)": ");
4051 char_u *tf = NULL;
4052 char_u *s = echo_string_core((typval_T *)(obj + 1) + i,
4053 &tf, numbuf, copyID, echo_style,
4054 restore_copyID, composite_val);
4055 if (s != NULL)
4056 ga_concat(&ga, s);
4057 vim_free(tf);
4058 if (s == NULL || did_echo_string_emsg)
4059 {
4060 ok = FAIL;
4061 break;
4062 }
4063 line_breakcheck();
4064 }
4065 ga_concat(&ga, (char_u *)"}");
4066 }
4067 if (ok == FAIL)
4068 {
4069 vim_free(ga.ga_data);
4070 return NULL;
4071 }
Yegappan Lakshmanancb848b62025-01-20 21:38:09 +01004072 ga_append(&ga, NUL);
Ernie Rael05ff4e42024-07-04 16:50:11 +02004073 return (char_u *)ga.ga_data;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01004074}
4075
4076/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02004077 * Return TRUE when the class "cl", its base class or one of the implemented
4078 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02004079 */
4080 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004081class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02004082{
4083 if (cl == other_cl)
4084 return TRUE;
4085
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004086 // Recursively check the base classes.
4087 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02004088 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004089 if (cl == other_cl)
4090 return TRUE;
4091 // Check the implemented interfaces and the super interfaces
4092 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02004093 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004094 class_T *intf = cl->class_interfaces_cl[i];
4095 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02004096 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02004097 if (intf == other_cl)
4098 return TRUE;
4099 // check the super interfaces
4100 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02004101 }
4102 }
LemonBoyafe04662023-08-23 21:08:11 +02004103 }
4104
4105 return FALSE;
4106}
4107
4108/*
Ernie Rael2025af12023-12-12 16:58:00 +01004109 * "instanceof(object, classinfo, ...)" function
LemonBoyafe04662023-08-23 21:08:11 +02004110 */
4111 void
4112f_instanceof(typval_T *argvars, typval_T *rettv)
4113{
4114 typval_T *object_tv = &argvars[0];
4115 typval_T *classinfo_tv = &argvars[1];
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02004116 class_T *c;
LemonBoyafe04662023-08-23 21:08:11 +02004117
4118 rettv->vval.v_number = VVAL_FALSE;
4119
4120 if (check_for_object_arg(argvars, 0) == FAIL
Ernie Rael2025af12023-12-12 16:58:00 +01004121 || check_for_class_or_typealias_args(argvars, 1) == FAIL)
LemonBoyafe04662023-08-23 21:08:11 +02004122 return;
4123
Ernie Rael3da696d2023-09-19 20:14:18 +02004124 if (object_tv->vval.v_object == NULL)
4125 return;
4126
Ernie Rael2025af12023-12-12 16:58:00 +01004127 for (; classinfo_tv->v_type != VAR_UNKNOWN; ++classinfo_tv)
LemonBoyafe04662023-08-23 21:08:11 +02004128 {
Ernie Rael2025af12023-12-12 16:58:00 +01004129 if (classinfo_tv->v_type == VAR_TYPEALIAS)
4130 c = classinfo_tv->vval.v_typealias->ta_type->tt_class;
4131 else
4132 c = classinfo_tv->vval.v_class;
4133
4134 if (class_instance_of(object_tv->vval.v_object->obj_class, c))
LemonBoyafe04662023-08-23 21:08:11 +02004135 {
Ernie Rael2025af12023-12-12 16:58:00 +01004136 rettv->vval.v_number = VVAL_TRUE;
4137 return;
LemonBoyafe04662023-08-23 21:08:11 +02004138 }
4139 }
LemonBoyafe04662023-08-23 21:08:11 +02004140}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00004141
4142#endif // FEAT_EVAL