blob: 7c7700ba19bd413b866a7ba8bc71121c4f672fa7 [file] [log] [blame]
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * vim9class.c: Vim9 script class support
12 */
13
14#define USING_FLOAT_STUFF
15#include "vim.h"
16
17#if defined(FEAT_EVAL) || defined(PROTO)
18
19// When not generating protos this is included in proto.h
20#ifdef PROTO
21# include "vim9.h"
22#endif
23
Yegappan Lakshmanane651e112023-09-04 07:51:01 +020024static class_T *first_class = NULL;
25static class_T *next_nonref_class = NULL;
26
27/*
28 * Call this function when a class has been created. It will be added to the
29 * list headed by "first_class".
30 */
31 static void
32class_created(class_T *cl)
33{
34 if (first_class != NULL)
35 {
36 cl->class_next_used = first_class;
37 first_class->class_prev_used = cl;
38 }
39 first_class = cl;
40}
41
42/*
43 * Call this function when a class has been cleared and is about to be freed.
44 * It is removed from the list headed by "first_class".
45 */
46 static void
47class_cleared(class_T *cl)
48{
49 if (cl->class_next_used != NULL)
50 cl->class_next_used->class_prev_used = cl->class_prev_used;
51 if (cl->class_prev_used != NULL)
52 cl->class_prev_used->class_next_used = cl->class_next_used;
53 else if (first_class == cl)
54 first_class = cl->class_next_used;
55
56 // update the next class to check if needed
57 if (cl == next_nonref_class)
58 next_nonref_class = cl->class_next_used;
59}
60
Bram Moolenaarc1c365c2022-12-04 20:13:24 +000061/*
Bram Moolenaard505d172022-12-18 21:42:55 +000062 * Parse a member declaration, both object and class member.
63 * Returns OK or FAIL. When OK then "varname_end" is set to just after the
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +020064 * variable name and "type_ret" is set to the declared or detected type.
Bram Moolenaard505d172022-12-18 21:42:55 +000065 * "init_expr" is set to the initialisation expression (allocated), if there is
Bram Moolenaar554d0312023-01-05 19:59:18 +000066 * one. For an interface "init_expr" is NULL.
Bram Moolenaard505d172022-12-18 21:42:55 +000067 */
68 static int
69parse_member(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020070 exarg_T *eap,
71 char_u *line,
72 char_u *varname,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020073 int has_public, // TRUE if "public" seen before "varname"
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020074 char_u **varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +020075 int *has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020076 garray_T *type_list,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020077 type_T **type_ret,
78 char_u **init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +000079{
80 *varname_end = to_name_end(varname, FALSE);
81 if (*varname == '_' && has_public)
82 {
RestorerZ7fe8f432023-09-24 23:21:24 +020083 semsg(_(e_public_variable_name_cannot_start_with_underscore_str), line);
Bram Moolenaard505d172022-12-18 21:42:55 +000084 return FAIL;
85 }
86
87 char_u *colon = skipwhite(*varname_end);
88 char_u *type_arg = colon;
89 type_T *type = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +020090 *has_type = FALSE;
Bram Moolenaard505d172022-12-18 21:42:55 +000091 if (*colon == ':')
92 {
93 if (VIM_ISWHITE(**varname_end))
94 {
95 semsg(_(e_no_white_space_allowed_before_colon_str), varname);
96 return FAIL;
97 }
98 if (!VIM_ISWHITE(colon[1]))
99 {
100 semsg(_(e_white_space_required_after_str_str), ":", varname);
101 return FAIL;
102 }
103 type_arg = skipwhite(colon + 1);
104 type = parse_type(&type_arg, type_list, TRUE);
105 if (type == NULL)
106 return FAIL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200107 *has_type = TRUE;
Bram Moolenaard505d172022-12-18 21:42:55 +0000108 }
109
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200110 char_u *init_arg = skipwhite(type_arg);
111 if (type == NULL && *init_arg != '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000112 {
113 emsg(_(e_type_or_initialization_required));
114 return FAIL;
115 }
116
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200117 if (init_expr == NULL && *init_arg == '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000118 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200119 emsg(_(e_cannot_initialize_variable_in_interface));
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200120 return FAIL;
121 }
122
123 if (*init_arg == '=')
124 {
125 evalarg_T evalarg;
126 char_u *expr_start, *expr_end;
127
128 if (!VIM_ISWHITE(init_arg[-1]) || !VIM_ISWHITE(init_arg[1]))
Bram Moolenaard505d172022-12-18 21:42:55 +0000129 {
130 semsg(_(e_white_space_required_before_and_after_str_at_str),
131 "=", type_arg);
132 return FAIL;
133 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200134 init_arg = skipwhite(init_arg + 1);
Bram Moolenaard505d172022-12-18 21:42:55 +0000135
Bram Moolenaard505d172022-12-18 21:42:55 +0000136 fill_evalarg_from_eap(&evalarg, eap, FALSE);
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200137 (void)skip_expr_concatenate(&init_arg, &expr_start, &expr_end, &evalarg);
Bram Moolenaard505d172022-12-18 21:42:55 +0000138
Yegappan Lakshmananac773182024-04-27 11:36:12 +0200139 init_arg = skipwhite(init_arg);
Yegappan Lakshmananfe55c312024-04-28 09:54:09 +0200140 if (*init_arg != NUL && !vim9_comment_start(init_arg))
Yegappan Lakshmananac773182024-04-27 11:36:12 +0200141 {
142 semsg(_(e_trailing_characters_str), init_arg);
143 return FAIL;
144 }
145
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +0200146 // No type specified for the member. Set it to "any" and the correct
147 // type will be set when the object is instantiated.
Bram Moolenaard505d172022-12-18 21:42:55 +0000148 if (type == NULL)
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200149 type = &t_any;
Bram Moolenaard505d172022-12-18 21:42:55 +0000150
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200151 *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
152 // Free the memory pointed by expr_start.
Bram Moolenaard505d172022-12-18 21:42:55 +0000153 clear_evalarg(&evalarg, NULL);
154 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200155 else if (!valid_declaration_type(type))
Bram Moolenaard505d172022-12-18 21:42:55 +0000156 return FAIL;
157
158 *type_ret = type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000159 return OK;
160}
161
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100162typedef struct oc_newmember_S oc_newmember_T;
163struct oc_newmember_S
164{
165 garray_T *gap;
166 char_u *varname;
167 char_u *varname_end;
168 int has_public;
169 int has_final;
170 int has_type;
171 type_T *type;
172 char_u *init_expr;
173};
174
Bram Moolenaard505d172022-12-18 21:42:55 +0000175/*
176 * Add a member to an object or a class.
177 * Returns OK when successful, "init_expr" will be consumed then.
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100178 * Returns OK on success and FAIL on memory allocation failure (caller might
179 * need to free "init_expr").
Bram Moolenaard505d172022-12-18 21:42:55 +0000180 */
181 static int
182add_member(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200183 garray_T *gap,
184 char_u *varname,
185 char_u *varname_end,
186 int has_public,
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100187 int has_final,
188 int has_const,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200189 int has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200190 type_T *type,
191 char_u *init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +0000192{
193 if (ga_grow(gap, 1) == FAIL)
194 return FAIL;
195 ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
196 m->ocm_name = vim_strnsave(varname, varname_end - varname);
=?UTF-8?q?Ola=20S=C3=B6der?=d8742472023-03-05 13:12:32 +0000197 m->ocm_access = has_public ? VIM_ACCESS_ALL
198 : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100199 if (has_final)
200 m->ocm_flags |= OCMFLAG_FINAL;
201 if (has_const)
202 m->ocm_flags |= OCMFLAG_CONST;
203 if (has_type)
204 m->ocm_flags |= OCMFLAG_HAS_TYPE;
Bram Moolenaard505d172022-12-18 21:42:55 +0000205 m->ocm_type = type;
206 if (init_expr != NULL)
207 m->ocm_init = init_expr;
208 ++gap->ga_len;
209 return OK;
210}
211
212/*
213 * Move the class or object members found while parsing a class into the class.
214 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000215 * "parent_members" points to the members in the parent class (if any)
216 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000217 * "members" will be set to the newly allocated array of members and
218 * "member_count" set to the number of members.
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100219 * Returns OK on success and FAIL on memory allocation failure.
Bram Moolenaard505d172022-12-18 21:42:55 +0000220 */
221 static int
222add_members_to_class(
223 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000224 ocmember_T *parent_members,
225 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000226 ocmember_T **members,
227 int *member_count)
228{
Bram Moolenaar83677162023-01-08 19:54:10 +0000229 *member_count = parent_count + gap->ga_len;
230 *members = *member_count == 0 ? NULL
231 : ALLOC_MULT(ocmember_T, *member_count);
232 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000233 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000234 for (int i = 0; i < parent_count; ++i)
235 {
236 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000237 ocmember_T *m = *members + i;
238 *m = parent_members[i];
239 m->ocm_name = vim_strsave(m->ocm_name);
240 if (m->ocm_init != NULL)
241 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000242 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000243 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000244 // new members are moved
245 mch_memmove(*members + parent_count,
246 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000247 VIM_CLEAR(gap->ga_data);
248 return OK;
249}
250
251/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000252 * Convert a member index "idx" of interface "itf" to the member index of class
253 * "cl" implementing that interface.
254 */
255 int
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200256object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000257{
Ernie Rael18143d32023-09-04 22:30:41 +0200258 if (idx >= (is_method ? itf->class_obj_method_count
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200259 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000260 {
261 siemsg("index %d out of range for interface %s", idx, itf->class_name);
262 return 0;
263 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200264
265 // If "cl" is the interface or the class that is extended, then the method
266 // index can be used directly and there is no need to search for the method
267 // index in one of the child classes.
268 if (cl == itf)
269 return idx;
270
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200271 itf2class_T *i2c = NULL;
272 int searching = TRUE;
273 int method_offset = 0;
274
Ernie Raelcf138d42023-09-06 20:45:03 +0200275 for (class_T *super = cl; super != NULL && searching;
276 super = super->class_extends)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200277 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200278 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200279 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200280 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
281 {
282 searching = FALSE;
283 break;
284 }
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200285 }
286 if (searching && is_method)
287 // The parent class methods are stored after the current class
288 // methods.
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200289 method_offset += super->class_obj_method_count_child;
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200290 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000291 if (i2c == NULL)
292 {
293 siemsg("class %s not found on interface %s",
294 cl->class_name, itf->class_name);
295 return 0;
296 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +0200297
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200298 // A table follows the i2c for the class
299 int *table = (int *)(i2c + 1);
300 // "method_offset" is 0, if method is in the current class. If method
301 // is in a parent class, then it is non-zero.
302 return table[idx] + method_offset;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000303}
304
305/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200306 * Check whether a class named "extends_name" is present. If the class is
307 * valid, then "extends_clp" is set with the class pointer.
308 * Returns TRUE if the class name "extends_names" is a valid class.
309 */
310 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200311validate_extends_class(
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100312 class_T *cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200313 char_u *extends_name,
314 class_T **extends_clp,
315 int is_class)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200316{
317 typval_T tv;
318 int success = FALSE;
319
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100320 if (STRCMP(cl->class_name, extends_name) == 0)
321 {
322 semsg(_(e_cannot_extend_str), extends_name);
323 return success;
324 }
325
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200326 tv.v_type = VAR_UNKNOWN;
327 if (eval_variable_import(extends_name, &tv) == FAIL)
328 {
329 semsg(_(e_class_name_not_found_str), extends_name);
330 return success;
331 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200332
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200333 if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100334 || (is_class && IS_INTERFACE(tv.vval.v_class))
335 || (!is_class && !IS_INTERFACE(tv.vval.v_class))
336 || (is_class && IS_ENUM(tv.vval.v_class)))
337 {
338 // a class cannot extend an interface
339 // an interface cannot extend a class
340 // a class cannot extend an enum.
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200341 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100342 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200343 else
344 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200345 class_T *extends_cl = tv.vval.v_class;
346 ++extends_cl->class_refcount;
347 *extends_clp = extends_cl;
348 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200349 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200350 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200351
352 return success;
353}
354
355/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200356 * Check method names in the parent class lineage to make sure the access is
357 * the same for overridden methods.
358 */
359 static int
360validate_extends_methods(
361 garray_T *objmethods_gap,
362 class_T *extends_cl)
363{
364 class_T *super = extends_cl;
365 int method_count = objmethods_gap->ga_len;
366 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
367
368 while (super != NULL)
369 {
370 int extends_method_count = super->class_obj_method_count_child;
371 if (extends_method_count == 0)
372 {
373 super = super->class_extends;
374 continue;
375 }
376
377 ufunc_T **extends_methods = super->class_obj_methods;
378
379 for (int i = 0; i < extends_method_count; i++)
380 {
381 char_u *pstr = extends_methods[i]->uf_name;
382 int extends_private = (*pstr == '_');
383 if (extends_private)
384 pstr++;
385
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200386 // When comparing the method names, ignore the access type (public
387 // and private methods are considered the same).
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200388 for (int j = 0; j < method_count; j++)
389 {
390 char_u *qstr = cl_fp[j]->uf_name;
391 int priv_method = (*qstr == '_');
392 if (priv_method)
393 qstr++;
394 if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
395 {
396 // Method access is different between the super class and
397 // the subclass
398 semsg(_(e_method_str_of_class_str_has_different_access),
399 cl_fp[j]->uf_name, super->class_name);
400 return FALSE;
401 }
402 }
403 }
404 super = super->class_extends;
405 }
406
407 return TRUE;
408}
409
410/*
411 * Check whether a object member variable in "objmembers_gap" is a duplicate of
412 * a member in any of the extended parent class lineage. Returns TRUE if there
413 * are no duplicates.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200414 */
415 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200416extends_check_dup_members(
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200417 garray_T *objmembers_gap,
418 class_T *extends_cl)
419{
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200420 int member_count = objmembers_gap->ga_len;
421 if (member_count == 0)
422 return TRUE;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200423
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200424 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
425
426 // Validate each member variable
427 for (int c_i = 0; c_i < member_count; c_i++)
428 {
429 class_T *p_cl = extends_cl;
430 ocmember_T *c_m = members + c_i;
431 char_u *pstr = (*c_m->ocm_name == '_')
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200432 ? c_m->ocm_name + 1 : c_m->ocm_name;
433
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200434 // Check in all the parent classes in the lineage
435 while (p_cl != NULL)
436 {
437 int p_member_count = p_cl->class_obj_member_count;
438 if (p_member_count == 0)
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200439 {
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200440 p_cl = p_cl->class_extends;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200441 continue;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200442 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200443 ocmember_T *p_members = p_cl->class_obj_members;
444
445 // Compare against all the members in the parent class
446 for (int p_i = 0; p_i < p_member_count; p_i++)
447 {
448 ocmember_T *p_m = p_members + p_i;
449 char_u *qstr = (*p_m->ocm_name == '_')
450 ? p_m->ocm_name + 1 : p_m->ocm_name;
451 if (STRCMP(pstr, qstr) == 0)
452 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200453 semsg(_(e_duplicate_variable_str), c_m->ocm_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200454 return FALSE;
455 }
456 }
457
458 p_cl = p_cl->class_extends;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200459 }
460 }
461
462 return TRUE;
463}
464
465/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200466 * Compare the variable type of interface variables in "objmembers_gap" against
467 * the variable in any of the extended super interface lineage. Used to
468 * compare the variable types when extending interfaces. Returns TRUE if the
469 * variable types are the same.
470 */
471 static int
472extends_check_intf_var_type(
473 garray_T *objmembers_gap,
474 class_T *extends_cl)
475{
476 int member_count = objmembers_gap->ga_len;
477 if (member_count == 0)
478 return TRUE;
479
480 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
481
482 // Validate each member variable
483 for (int c_i = 0; c_i < member_count; c_i++)
484 {
485 class_T *p_cl = extends_cl;
486 ocmember_T *c_m = members + c_i;
487 int var_found = FALSE;
488
489 // Check in all the parent classes in the lineage
490 while (p_cl != NULL && !var_found)
491 {
492 int p_member_count = p_cl->class_obj_member_count;
493 if (p_member_count == 0)
494 {
495 p_cl = p_cl->class_extends;
496 continue;
497 }
498 ocmember_T *p_members = p_cl->class_obj_members;
499
500 // Compare against all the members in the parent class
501 for (int p_i = 0; p_i < p_member_count; p_i++)
502 {
503 where_T where = WHERE_INIT;
504 ocmember_T *p_m = p_members + p_i;
505
506 if (STRCMP(p_m->ocm_name, c_m->ocm_name) != 0)
507 continue;
508
509 // Ensure the type is matching.
510 where.wt_func_name = (char *)c_m->ocm_name;
511 where.wt_kind = WT_MEMBER;
512
513 if (check_type(p_m->ocm_type, c_m->ocm_type, TRUE,
514 where) == FAIL)
515 return FALSE;
516
517 var_found = TRUE;
518 }
519
520 p_cl = p_cl->class_extends;
521 }
522 }
523
524 return TRUE;
525}
526
527/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200528 * When extending an abstract class, check whether all the abstract methods in
529 * the parent class are implemented. Returns TRUE if all the methods are
530 * implemented.
531 */
532 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200533validate_abstract_class_methods(
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200534 garray_T *classmethods_gap,
535 garray_T *objmethods_gap,
536 class_T *extends_cl)
537{
538 for (int loop = 1; loop <= 2; ++loop)
539 {
540 // loop == 1: check class methods
541 // loop == 2: check object methods
542 int extends_method_count = loop == 1
543 ? extends_cl->class_class_function_count
544 : extends_cl->class_obj_method_count;
545 if (extends_method_count == 0)
546 continue;
547
548 ufunc_T **extends_methods = loop == 1
549 ? extends_cl->class_class_functions
550 : extends_cl->class_obj_methods;
551
552 int method_count = loop == 1 ? classmethods_gap->ga_len
553 : objmethods_gap->ga_len;
554 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
555 ? classmethods_gap->ga_data
556 : objmethods_gap->ga_data);
557
558 for (int i = 0; i < extends_method_count; i++)
559 {
560 ufunc_T *uf = extends_methods[i];
Yegappan Lakshmanan1db15142023-09-19 20:34:05 +0200561 if (!IS_ABSTRACT_METHOD(uf))
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200562 continue;
563
564 int method_found = FALSE;
565
566 for (int j = 0; j < method_count; j++)
567 {
568 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
569 {
570 method_found = TRUE;
571 break;
572 }
573 }
574
575 if (!method_found)
576 {
577 semsg(_(e_abstract_method_str_not_found), uf->uf_name);
578 return FALSE;
579 }
580 }
581 }
582
583 return TRUE;
584}
585
586/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200587 * Returns TRUE if the interface variable "if_var" is present in the list of
588 * variables in "cl_mt" or in the parent lineage of one of the extended classes
589 * in "extends_cl". For a class variable, 'is_class_var' is TRUE.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200590 */
591 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200592intf_variable_present(
593 char_u *intf_class_name,
594 ocmember_T *if_var,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200595 ocmember_T *cl_mt,
596 int cl_member_count,
597 class_T *extends_cl)
598{
599 int variable_present = FALSE;
600
601 for (int cl_i = 0; cl_i < cl_member_count; ++cl_i)
602 {
603 ocmember_T *m = &cl_mt[cl_i];
604 where_T where = WHERE_INIT;
605
606 if (STRCMP(if_var->ocm_name, m->ocm_name) != 0)
607 continue;
608
609 // Ensure the access type is same
610 if (if_var->ocm_access != m->ocm_access)
611 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200612 semsg(_(e_variable_str_of_interface_str_has_different_access),
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200613 if_var->ocm_name, intf_class_name);
614 return FALSE;
615 }
616
617 // Ensure the type is matching.
618 if (m->ocm_type == &t_any)
619 {
620 // variable type is not specified. Use the variable type in the
621 // interface.
622 m->ocm_type = if_var->ocm_type;
623 }
624 else
625 {
626 where.wt_func_name = (char *)m->ocm_name;
627 where.wt_kind = WT_MEMBER;
628 if (check_type(if_var->ocm_type, m->ocm_type, TRUE,
629 where) == FAIL)
630 return FALSE;
631 }
632
633 variable_present = TRUE;
634 break;
635 }
636
637 if (!variable_present && extends_cl != NULL)
638 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200639 int ext_cl_count = extends_cl->class_obj_member_count;
640 ocmember_T *ext_cl_mt = extends_cl->class_obj_members;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200641 return intf_variable_present(intf_class_name, if_var,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200642 ext_cl_mt, ext_cl_count,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200643 extends_cl->class_extends);
644 }
645
646 return variable_present;
647}
648
649/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200650 * Check the variables of the interface class "ifcl" match object variables
651 * ("objmembers_gap") of a class.
652 * Returns TRUE if the object variables names are valid.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200653 */
654 static int
655validate_interface_variables(
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200656 char_u *intf_class_name,
657 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200658 garray_T *objmembers_gap,
659 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200660{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200661 int if_count = ifcl->class_obj_member_count;
662 if (if_count == 0)
663 return TRUE;
664
665 ocmember_T *if_ms = ifcl->class_obj_members;
666 ocmember_T *cl_ms = (ocmember_T *)(objmembers_gap->ga_data);
667 int cl_count = objmembers_gap->ga_len;
668 for (int if_i = 0; if_i < if_count; ++if_i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200669 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200670 if (!intf_variable_present(intf_class_name, &if_ms[if_i], cl_ms,
671 cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200672 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200673 semsg(_(e_variable_str_of_interface_str_not_implemented),
674 if_ms[if_i].ocm_name, intf_class_name);
675 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200676 }
677 }
678
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200679 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200680}
681
682/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200683 * Returns TRUE if the method signature of "if_method" and "cl_method" matches.
684 */
685 static int
686intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method)
687{
688 where_T where = WHERE_INIT;
689
690 // Ensure the type is matching.
691 where.wt_func_name = (char *)if_method->uf_name;
692 where.wt_kind = WT_METHOD;
693 if (check_type(if_method->uf_func_type, cl_method->uf_func_type, TRUE,
694 where) == FAIL)
695 return FALSE;
696
697 return TRUE;
698}
699
700/*
701 * Returns TRUE if the interface method "if_ufunc" is present in the list of
702 * methods in "cl_fp" or in the parent lineage of one of the extended classes
703 * in "extends_cl". For a class method, 'is_class_method' is TRUE.
704 */
705 static int
706intf_method_present(
707 ufunc_T *if_ufunc,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200708 ufunc_T **cl_fp,
709 int cl_count,
710 class_T *extends_cl)
711{
712 int method_present = FALSE;
713
714 for (int cl_i = 0; cl_i < cl_count; ++cl_i)
715 {
716 char_u *cl_name = cl_fp[cl_i]->uf_name;
717 if (STRCMP(if_ufunc->uf_name, cl_name) == 0)
718 {
719 // Ensure the type is matching.
720 if (!intf_method_type_matches(if_ufunc, cl_fp[cl_i]))
721 return FALSE;
722 method_present = TRUE;
723 break;
724 }
725 }
726
727 if (!method_present && extends_cl != NULL)
728 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200729 ufunc_T **ext_cl_fp = (ufunc_T **)(extends_cl->class_obj_methods);
730 int ext_cl_count = extends_cl->class_obj_method_count;
731 return intf_method_present(if_ufunc, ext_cl_fp, ext_cl_count,
732 extends_cl->class_extends);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200733 }
734
735 return method_present;
736}
737
738/*
739 * Validate that a new class implements all the class/instance methods in the
740 * interface "ifcl". The new class methods are in "classfunctions_gap" and the
741 * new object methods are in "objmemthods_gap". Also validates the method
742 * types.
743 * Returns TRUE if all the interface class/object methods are implemented in
744 * the new class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200745 */
746 static int
747validate_interface_methods(
748 char_u *intf_class_name,
749 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200750 garray_T *objmethods_gap,
751 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200752{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200753 int if_count = ifcl->class_obj_method_count;
754 if (if_count == 0)
755 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200756
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200757 ufunc_T **if_fp = ifcl->class_obj_methods;
758 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
759 int cl_count = objmethods_gap->ga_len;
760 for (int if_i = 0; if_i < if_count; ++if_i)
761 {
762 char_u *if_name = if_fp[if_i]->uf_name;
763
764 if (!intf_method_present(if_fp[if_i], cl_fp, cl_count, extends_cl))
765 {
766 semsg(_(e_method_str_of_interface_str_not_implemented),
767 if_name, intf_class_name);
768 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200769 }
770 }
771
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200772 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200773}
774
775/*
776 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200777 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200778 * object methods and object members in the new class are in
779 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
780 * "objmembers_gap" respectively.
781 */
782 static int
783validate_implements_classes(
784 garray_T *impl_gap,
785 class_T **intf_classes,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200786 garray_T *objmethods_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200787 garray_T *objmembers_gap,
788 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200789{
790 int success = TRUE;
791
792 for (int i = 0; i < impl_gap->ga_len && success; ++i)
793 {
794 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
795 typval_T tv;
796 tv.v_type = VAR_UNKNOWN;
797 if (eval_variable_import(impl, &tv) == FAIL)
798 {
799 semsg(_(e_interface_name_not_found_str), impl);
800 success = FALSE;
801 break;
802 }
803
804 if (tv.v_type != VAR_CLASS
805 || tv.vval.v_class == NULL
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100806 || !IS_INTERFACE(tv.vval.v_class))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200807 {
808 semsg(_(e_not_valid_interface_str), impl);
809 success = FALSE;
810 clear_tv(&tv);
811 break;
812 }
813
814 class_T *ifcl = tv.vval.v_class;
815 intf_classes[i] = ifcl;
816 ++ifcl->class_refcount;
817
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200818 // check the variables of the interface match the members of the class
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200819 success = validate_interface_variables(impl, ifcl, objmembers_gap,
820 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200821
822 // check the functions/methods of the interface match the
823 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200824 if (success)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200825 success = validate_interface_methods(impl, ifcl, objmethods_gap,
826 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200827 clear_tv(&tv);
828 }
829
830 return success;
831}
832
833/*
834 * Check no function argument name is used as a class member.
835 * (Object members are always accessed with "this." prefix, so no need
836 * to check them.)
837 */
838 static int
839check_func_arg_names(
840 garray_T *classfunctions_gap,
841 garray_T *objmethods_gap,
842 garray_T *classmembers_gap)
843{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200844 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200845 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200846 {
847 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
848
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200849 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200850 {
851 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
852
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200853 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200854 {
855 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
856 garray_T *mgap = classmembers_gap;
857
858 // Check all the class member names
859 for (int mi = 0; mi < mgap->ga_len; ++mi)
860 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200861 char_u *mname =
862 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200863 if (STRCMP(aname, mname) == 0)
864 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200865 if (uf->uf_script_ctx.sc_sid > 0)
866 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
867
868 semsg(_(e_argument_already_declared_in_class_str),
869 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200870
871 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200872 }
873 }
874 }
875 }
876 }
877
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200878 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200879}
880
881/*
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200882 * Returns TRUE if 'varname' is a reserved keyword name
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200883 */
884 static int
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200885is_reserved_varname(char_u *varname, char_u *varname_end)
886{
887 int reserved = FALSE;
888 char_u save_varname_end = *varname_end;
889 *varname_end = NUL;
890
891 reserved = check_reserved_name(varname, FALSE) == FAIL;
892
893 *varname_end = save_varname_end;
894
895 return reserved;
896}
897
898/*
899 * Returns TRUE if the variable "varname" is already defined either as a class
900 * variable or as an object variable.
901 */
902 static int
903is_duplicate_variable(
904 garray_T *class_members,
905 garray_T *obj_members,
906 char_u *varname,
907 char_u *varname_end)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200908{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200909 char_u *name = vim_strnsave(varname, varname_end - varname);
910 char_u *pstr = (*name == '_') ? name + 1 : name;
911 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200912
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200913 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200914 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200915 // loop == 1: class variables, loop == 2: object variables
916 garray_T *vgap = (loop == 1) ? class_members : obj_members;
917 for (int i = 0; i < vgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200918 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200919 ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i;
920 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1
921 : m->ocm_name;
922 if (STRCMP(pstr, qstr) == 0)
923 {
924 semsg(_(e_duplicate_variable_str), name);
925 dup = TRUE;
926 break;
927 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200928 }
929 }
930
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200931 vim_free(name);
932 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200933}
934
935/*
936 * Returns TRUE if the method "name" is already defined.
937 */
938 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200939is_duplicate_method(
940 garray_T *classmethods_gap,
941 garray_T *objmethods_gap,
942 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200943{
944 char_u *pstr = (*name == '_') ? name + 1 : name;
945
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200946 // loop 1: class methods, loop 2: object methods
947 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200948 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200949 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
950 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200951 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200952 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
953 char_u *qstr = *n == '_' ? n + 1 : n;
954 if (STRCMP(pstr, qstr) == 0)
955 {
956 semsg(_(e_duplicate_function_str), name);
957 return TRUE;
958 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200959 }
960 }
961
962 return FALSE;
963}
964
965/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200966 * Returns TRUE if the constructor is valid.
967 */
968 static int
969is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
970{
971 // Constructors are not allowed in abstract classes.
972 if (is_abstract)
973 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200974 emsg(_(e_cannot_define_new_method_in_abstract_class));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200975 return FALSE;
976 }
977 // A constructor is always static, no need to define it so.
978 if (has_static)
979 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200980 emsg(_(e_cannot_define_new_method_as_static));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200981 return FALSE;
982 }
983 // A return type should not be specified for the new()
984 // constructor method.
985 if (uf->uf_ret_type->tt_type != VAR_VOID)
986 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200987 emsg(_(e_cannot_use_a_return_type_with_new_method));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200988 return FALSE;
989 }
990 return TRUE;
991}
992
993/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +0100994 * Returns TRUE if 'uf' is a supported builtin method and has the correct
995 * method signature.
996 */
997 static int
998object_check_builtin_method_sig(ufunc_T *uf)
999{
1000 char_u *name = uf->uf_name;
1001 int valid = FALSE;
1002 type_T method_sig;
1003 type_T method_rt;
1004 where_T where = WHERE_INIT;
1005
1006 // validate the method signature
1007 CLEAR_FIELD(method_sig);
1008 CLEAR_FIELD(method_rt);
1009 method_sig.tt_type = VAR_FUNC;
1010
1011 if (STRCMP(name, "len") == 0)
1012 {
1013 // def __len(): number
1014 method_rt.tt_type = VAR_NUMBER;
1015 method_sig.tt_member = &method_rt;
1016 valid = TRUE;
1017 }
1018 else if (STRCMP(name, "empty") == 0)
1019 {
1020 // def __empty(): bool
1021 method_rt.tt_type = VAR_BOOL;
1022 method_sig.tt_member = &method_rt;
1023 valid = TRUE;
1024 }
1025 else if (STRCMP(name, "string") == 0)
1026 {
1027 // def __string(): string
1028 method_rt.tt_type = VAR_STRING;
1029 method_sig.tt_member = &method_rt;
1030 valid = TRUE;
1031 }
1032 else
1033 semsg(_(e_builtin_object_method_str_not_supported), uf->uf_name);
1034
1035 where.wt_func_name = (char *)uf->uf_name;
1036 where.wt_kind = WT_METHOD;
1037 if (valid && !check_type(&method_sig, uf->uf_func_type, TRUE, where))
1038 valid = FALSE;
1039
1040 return valid;
1041}
1042
1043/*
1044 * Returns TRUE if "funcname" is a supported builtin object method name
1045 */
1046 int
1047is_valid_builtin_obj_methodname(char_u *funcname)
1048{
1049 switch (funcname[0])
1050 {
1051 case 'e':
1052 return STRNCMP(funcname, "empty", 5) == 0;
1053
1054 case 'l':
1055 return STRNCMP(funcname, "len", 3) == 0;
1056
1057 case 'n':
1058 return STRNCMP(funcname, "new", 3) == 0;
1059
1060 case 's':
1061 return STRNCMP(funcname, "string", 6) == 0;
1062 }
1063
1064 return FALSE;
1065}
1066
1067
1068/*
1069 * Returns the builtin method "name" in object "obj". Returns NULL if the
1070 * method is not found.
1071 */
1072 ufunc_T *
1073class_get_builtin_method(
1074 class_T *cl,
1075 class_builtin_T builtin_method,
1076 int *method_idx)
1077{
1078 *method_idx = -1;
1079
1080 if (cl == NULL)
1081 return NULL;
1082
1083 *method_idx = cl->class_builtin_methods[builtin_method];
1084 return *method_idx != -1 ? cl->class_obj_methods[*method_idx] : NULL;
1085}
1086
1087/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001088 * Update the interface class lookup table for the member index on the
1089 * interface to the member index in the class implementing the interface.
1090 * And a lookup table for the object method index on the interface
1091 * to the object method index in the class implementing the interface.
1092 * This is also used for updating the lookup table for the extended class
1093 * hierarchy.
1094 */
1095 static int
1096update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001097 class_T *ifcl,
1098 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001099 garray_T *objmethods,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001100 int pobj_method_offset)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001101{
1102 if (ifcl == NULL)
1103 return OK;
1104
1105 // Table for members.
1106 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001107 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001108 if (if2cl == NULL)
1109 return FAIL;
1110 if2cl->i2c_next = ifcl->class_itf2class;
1111 ifcl->class_itf2class = if2cl;
1112 if2cl->i2c_class = cl;
1113 if2cl->i2c_is_method = FALSE;
1114
1115 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
1116 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
1117 {
1118 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001119 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001120 {
1121 int *table = (int *)(if2cl + 1);
1122 table[if_i] = cl_i;
1123 break;
1124 }
1125 }
1126
1127 // Table for methods.
1128 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001129 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001130 if (if2cl == NULL)
1131 return FAIL;
1132 if2cl->i2c_next = ifcl->class_itf2class;
1133 ifcl->class_itf2class = if2cl;
1134 if2cl->i2c_class = cl;
1135 if2cl->i2c_is_method = TRUE;
1136
1137 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
1138 {
1139 int done = FALSE;
1140 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
1141 {
1142 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001143 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001144 {
1145 int *table = (int *)(if2cl + 1);
1146 table[if_i] = cl_i;
1147 done = TRUE;
1148 break;
1149 }
1150 }
1151
1152 // extended class object method is not overridden by the child class.
1153 // Keep the method declared in one of the parent classes in the
1154 // lineage.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001155 if (!done)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001156 {
1157 // If "ifcl" is not the immediate parent of "cl", then search in
1158 // the intermediate parent classes.
1159 if (cl->class_extends != ifcl)
1160 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001161 class_T *parent = cl->class_extends;
1162 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001163
1164 while (!done && parent != NULL && parent != ifcl)
1165 {
1166
1167 for (int cl_i = 0;
1168 cl_i < parent->class_obj_method_count_child; ++cl_i)
1169 {
1170 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
1171 parent->class_obj_methods[cl_i]->uf_name)
1172 == 0)
1173 {
1174 int *table = (int *)(if2cl + 1);
1175 table[if_i] = method_offset + cl_i;
1176 done = TRUE;
1177 break;
1178 }
1179 }
1180 method_offset += parent->class_obj_method_count_child;
1181 parent = parent->class_extends;
1182 }
1183 }
1184
1185 if (!done)
1186 {
1187 int *table = (int *)(if2cl + 1);
1188 table[if_i] = pobj_method_offset + if_i;
1189 }
1190 }
1191 }
1192
1193 return OK;
1194}
1195
1196/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001197 * Update the member and object method lookup tables for a new class in the
1198 * interface class.
1199 * For each interface add a lookup table for the member index on the interface
1200 * to the member index in the new class. And a lookup table for the object
1201 * method index on the interface to the object method index in the new class.
1202 */
1203 static int
1204add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
1205{
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001206 // update the lookup table for all the implemented interfaces
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001207 for (int i = 0; i < cl->class_interface_count; ++i)
1208 {
1209 class_T *ifcl = cl->class_interfaces_cl[i];
1210
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001211 // update the lookup table for this interface and all its super
1212 // interfaces.
1213 while (ifcl != NULL)
1214 {
1215 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
1216 0) == FAIL)
1217 return FAIL;
1218 ifcl = ifcl->class_extends;
1219 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001220 }
1221
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001222 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001223 if (extends_cl != NULL)
1224 {
1225 class_T *pclass = extends_cl;
1226 int pobj_method_offset = objmethods_gap->ga_len;
1227
1228 // Update the entire lineage of extended classes.
1229 while (pclass != NULL)
1230 {
1231 if (update_member_method_lookup_table(pclass, cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001232 objmethods_gap, pobj_method_offset) == FAIL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001233 return FAIL;
1234
1235 pobj_method_offset += pclass->class_obj_method_count_child;
1236 pclass = pclass->class_extends;
1237 }
1238 }
1239
1240 return OK;
1241}
1242
1243/*
1244 * Add class members to a new class. Allocate a typval for each class member
1245 * and initialize it.
1246 */
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001247 static int
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001248add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001249{
1250 // Allocate a typval for each class member and initialize it.
1251 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
1252 cl->class_class_member_count);
1253 if (cl->class_members_tv == NULL)
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001254 return FAIL;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001255
1256 for (int i = 0; i < cl->class_class_member_count; ++i)
1257 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001258 ocmember_T *m = &cl->class_class_members[i];
1259 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001260 if (m->ocm_init != NULL)
1261 {
1262 typval_T *etv = eval_expr(m->ocm_init, eap);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001263 if (etv == NULL)
1264 return FAIL;
1265
1266 if (m->ocm_type->tt_type == VAR_ANY
1267 && !(m->ocm_flags & OCMFLAG_HAS_TYPE)
1268 && etv->v_type != VAR_SPECIAL)
1269 // If the member variable type is not yet set, then use
1270 // the initialization expression type.
1271 m->ocm_type = typval2type(etv, get_copyID(),
1272 type_list_gap,
1273 TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
1274 *tv = *etv;
1275 vim_free(etv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001276 }
1277 else
1278 {
1279 // TODO: proper default value
1280 tv->v_type = m->ocm_type->tt_type;
1281 tv->vval.v_string = NULL;
1282 }
LemonBoyf4af3312024-07-04 13:43:12 +02001283 set_tv_type(tv, m->ocm_type);
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001284 if (m->ocm_flags & OCMFLAG_CONST)
1285 item_lock(tv, DICT_MAXNEST, TRUE, TRUE);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001286 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001287
1288 return OK;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001289}
1290
1291/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001292 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001293 */
1294 static void
1295add_default_constructor(
1296 class_T *cl,
1297 garray_T *classfunctions_gap,
1298 garray_T *type_list_gap)
1299{
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001300 garray_T fga;
1301 int is_enum = IS_ENUM(cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001302
1303 ga_init2(&fga, 1, 1000);
1304 ga_concat(&fga, (char_u *)"new(");
1305 for (int i = 0; i < cl->class_obj_member_count; ++i)
1306 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001307 if (i < 2 && is_enum)
1308 // The first two object variables in an enum are the enum value
1309 // name and ordinal. Don't initialize these object variables in
1310 // the default constructor as they are already initialized right
1311 // after creating the object.
1312 continue;
1313
1314 if (i > (is_enum ? 2 : 0))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001315 ga_concat(&fga, (char_u *)", ");
1316 ga_concat(&fga, (char_u *)"this.");
1317 ocmember_T *m = cl->class_obj_members + i;
1318 ga_concat(&fga, (char_u *)m->ocm_name);
1319 ga_concat(&fga, (char_u *)" = v:none");
1320 }
1321 ga_concat(&fga, (char_u *)")\nenddef\n");
1322 ga_append(&fga, NUL);
1323
1324 exarg_T fea;
1325 CLEAR_FIELD(fea);
1326 fea.cmdidx = CMD_def;
1327 fea.cmd = fea.arg = fga.ga_data;
1328
1329 garray_T lines_to_free;
1330 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1331
h-eastb895b0f2023-09-24 15:46:31 +02001332 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
1333 cl->class_obj_members, cl->class_obj_member_count);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001334
1335 ga_clear_strings(&lines_to_free);
1336 vim_free(fga.ga_data);
1337
1338 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1339 {
1340 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001341 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001342 ++classfunctions_gap->ga_len;
1343
1344 nf->uf_flags |= FC_NEW;
1345 nf->uf_ret_type = get_type_ptr(type_list_gap);
1346 if (nf->uf_ret_type != NULL)
1347 {
1348 nf->uf_ret_type->tt_type = VAR_OBJECT;
1349 nf->uf_ret_type->tt_class = cl;
1350 nf->uf_ret_type->tt_argcount = 0;
1351 nf->uf_ret_type->tt_args = NULL;
1352 }
1353 }
1354}
1355
1356/*
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001357 * Add the class methods and object methods to the new class "cl".
1358 * When extending a class "extends_cl", add the instance methods from the
1359 * parent class also.
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001360 * Returns OK on success and FAIL on memory allocation failure.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001361 */
1362 static int
1363add_classfuncs_objmethods(
1364 class_T *cl,
1365 class_T *extends_cl,
1366 garray_T *classfunctions_gap,
1367 garray_T *objmethods_gap)
1368{
1369 // loop 1: class functions, loop 2: object methods
1370 for (int loop = 1; loop <= 2; ++loop)
1371 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001372 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1373 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001374 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001375 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001376 : &cl->class_obj_methods;
1377
1378 int parent_count = 0;
1379 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001380 // Include object methods from the parent.
1381 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001382 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001383 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001384 : extends_cl->class_obj_method_count;
1385
1386 *fcount = parent_count + gap->ga_len;
1387 if (*fcount == 0)
1388 {
1389 *fup = NULL;
1390 continue;
1391 }
1392 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1393 if (*fup == NULL)
1394 return FAIL;
1395
1396 if (gap->ga_len != 0)
1397 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001398 VIM_CLEAR(gap->ga_data);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001399 if (loop == 1)
1400 cl->class_class_function_count_child = gap->ga_len;
1401 else
1402 cl->class_obj_method_count_child = gap->ga_len;
1403
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001404 if (loop == 2)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001405 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001406 // Copy instance methods from the parent.
1407
1408 for (int i = 0; i < parent_count; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001409 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001410 // Can't use the same parent function, because "uf_class" is
1411 // different and compilation will have a different result.
1412 // Put them after the functions in the current class, object
1413 // methods may be overruled, then "super.Method()" is used to
1414 // find a method from the parent.
1415 ufunc_T *pf = (extends_cl->class_obj_methods)[i];
1416 (*fup)[gap->ga_len + i] = copy_function(pf);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001417
1418 // If the child class overrides a function from the parent
1419 // the signature must be equal.
1420 char_u *pname = pf->uf_name;
1421 for (int ci = 0; ci < gap->ga_len; ++ci)
1422 {
1423 ufunc_T *cf = (*fup)[ci];
1424 char_u *cname = cf->uf_name;
1425 if (STRCMP(pname, cname) == 0)
1426 {
1427 where_T where = WHERE_INIT;
1428 where.wt_func_name = (char *)pname;
1429 where.wt_kind = WT_METHOD;
1430 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1431 TRUE, where);
1432 }
1433 }
1434 }
1435 }
1436
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001437 // Set the class pointer on all the functions and object methods.
1438 for (int i = 0; i < *fcount; ++i)
1439 {
1440 ufunc_T *fp = (*fup)[i];
1441 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001442 if (i < gap->ga_len)
1443 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001444 if (loop == 2)
1445 fp->uf_flags |= FC_OBJECT;
1446 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001447
1448 ga_clear(gap);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001449 }
1450
1451 return OK;
1452}
1453
1454/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001455 * Update the index of object methods called by builtin functions.
1456 */
1457 static void
1458update_builtin_method_index(class_T *cl)
1459{
1460 int i;
1461
1462 for (i = 0; i < CLASS_BUILTIN_MAX; i++)
1463 cl->class_builtin_methods[i] = -1;
1464
1465 for (i = 0; i < cl->class_obj_method_count; i++)
1466 {
1467 ufunc_T *uf = cl->class_obj_methods[i];
1468
1469 if (cl->class_builtin_methods[CLASS_BUILTIN_STRING] == -1
1470 && STRCMP(uf->uf_name, "string") == 0)
1471 cl->class_builtin_methods[CLASS_BUILTIN_STRING] = i;
1472 else if (cl->class_builtin_methods[CLASS_BUILTIN_EMPTY] == -1 &&
1473 STRCMP(uf->uf_name, "empty") == 0)
1474 cl->class_builtin_methods[CLASS_BUILTIN_EMPTY] = i;
1475 else if (cl->class_builtin_methods[CLASS_BUILTIN_LEN] == -1 &&
1476 STRCMP(uf->uf_name, "len") == 0)
1477 cl->class_builtin_methods[CLASS_BUILTIN_LEN] = i;
1478 }
1479}
1480
1481/*
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001482 * Return the end of the class name starting at "arg". Valid characters in a
1483 * class name are alphanumeric characters and "_". Also handles imported class
1484 * names.
1485 */
1486 static char_u *
1487find_class_name_end(char_u *arg)
1488{
1489 char_u *end = arg;
1490
1491 while (ASCII_ISALNUM(*end) || *end == '_'
1492 || (*end == '.' && (ASCII_ISALNUM(end[1]) || end[1] == '_')))
1493 ++end;
1494
1495 return end;
1496}
1497
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001498/*
1499 * Returns TRUE if the enum value "varname" is already defined.
1500 */
1501 static int
1502is_duplicate_enum(
1503 garray_T *enum_gap,
1504 char_u *varname,
1505 char_u *varname_end)
1506{
1507 char_u *name = vim_strnsave(varname, varname_end - varname);
1508 int dup = FALSE;
1509
1510 for (int i = 0; i < enum_gap->ga_len; ++i)
1511 {
1512 ocmember_T *m = ((ocmember_T *)enum_gap->ga_data) + i;
1513 if (STRCMP(name, m->ocm_name) == 0)
1514 {
1515 semsg(_(e_duplicate_enum_str), name);
1516 dup = TRUE;
1517 break;
1518 }
1519 }
1520
1521 vim_free(name);
1522 return dup;
1523}
1524
1525/*
1526 * Parse the enum values in "line" separated by comma and add them to "gap".
1527 * If the last enum value is found, then "enum_end" is set to TRUE.
1528 */
1529 static int
1530enum_parse_values(
1531 exarg_T *eap,
1532 class_T *en,
1533 char_u *line,
1534 garray_T *gap,
1535 int *num_enum_values,
1536 int *enum_end)
1537{
1538 evalarg_T evalarg;
1539 char_u *p = line;
1540 char initexpr_buf[1024];
1541 char_u last_char = NUL;
1542 int rc = OK;
1543
1544 fill_evalarg_from_eap(&evalarg, eap, FALSE);
1545
1546 int did_emsg_before = did_emsg;
1547 while (*p != NUL)
1548 {
1549 // ignore comment
1550 if (*p == '#')
1551 break;
1552
1553 if (!eval_isnamec1(*p))
1554 {
1555 semsg(_(e_invalid_enum_value_declaration_str), p);
1556 break;
1557 }
1558
1559 char_u *eni_name_start = p;
1560 char_u *eni_name_end = to_name_end(p, FALSE);
1561
1562 if (is_duplicate_enum(gap, eni_name_start, eni_name_end))
1563 break;
1564
1565 p = skipwhite(eni_name_end);
1566
1567 char_u *init_expr = NULL;
1568 if (*p == '(')
1569 {
1570 if (VIM_ISWHITE(p[-1]))
1571 {
1572 semsg(_(e_no_white_space_allowed_before_str_str), "(", line);
1573 break;
1574 }
1575
1576 char_u *expr_start, *expr_end;
1577
1578 p = eni_name_start;
1579 (void)skip_expr_concatenate(&p, &expr_start, &expr_end, &evalarg);
1580
1581 while (*expr_start && *expr_start != '(')
1582 expr_start++;
1583
1584 if (expr_end > expr_start)
1585 init_expr = vim_strnsave(expr_start, expr_end - expr_start);
1586 }
1587
1588 if (init_expr == NULL)
1589 vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new()",
1590 en->class_name);
1591 else
1592 {
1593 vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new%s",
1594 en->class_name, init_expr);
1595 vim_free(init_expr);
1596 }
1597 if (add_member(gap, eni_name_start, eni_name_end, FALSE,
1598 TRUE, TRUE, TRUE, &en->class_object_type,
1599 vim_strsave((char_u *)initexpr_buf)) == FAIL)
1600 break;
1601
1602 ++*num_enum_values;
1603
1604 if (*p != '#')
1605 last_char = *p;
1606
1607 if (*p != NUL && *p != ',')
1608 break;
1609
1610 if (*p == ',')
1611 {
1612 if (!IS_WHITE_OR_NUL(p[1]))
1613 {
1614 semsg(_(e_white_space_required_after_str_str), ",", line);
1615 break;
1616 }
1617 if (VIM_ISWHITE(p[-1]))
1618 {
1619 semsg(_(e_no_white_space_allowed_before_str_str), ",", line);
1620 break;
1621 }
1622 p = skipwhite(p + 1);
1623 }
1624 }
1625
Doug Kearnsdbe39ed2025-01-04 17:12:24 +01001626 p = skipwhite(p);
1627
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001628 if (*p != NUL && *p != '#')
1629 {
1630 if (did_emsg == did_emsg_before)
1631 semsg(_(e_missing_comma_before_argument_str), p);
1632 rc = FAIL;
1633 }
1634
1635 if (last_char != ',')
1636 // last enum value should not be terminated by ","
1637 *enum_end = TRUE;
1638
1639 // Free the memory pointed by expr_start.
1640 clear_evalarg(&evalarg, NULL);
1641
1642 return rc;
1643}
1644
1645/*
1646 * Add the "values" class variable (List of enum value objects) to the enum
1647 * class "en"
1648 */
1649 static int
1650enum_add_values_member(
1651 class_T *en,
1652 garray_T *gap,
1653 int num_enum_values,
1654 garray_T *type_list_gap)
1655{
1656 garray_T fga;
1657 int rc = FAIL;
1658
1659 ga_init2(&fga, 1, 1000);
1660 ga_concat(&fga, (char_u *)"[");
1661 for (int i = 0; i < num_enum_values; ++i)
1662 {
1663 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
1664
1665 if (i > 0)
1666 ga_concat(&fga, (char_u *)", ");
1667 ga_concat(&fga, en->class_name);
1668 ga_concat(&fga, (char_u *)".");
1669 ga_concat(&fga, (char_u *)m->ocm_name);
1670 }
1671 ga_concat(&fga, (char_u *)"]");
1672 ga_append(&fga, NUL);
1673
1674 char_u *varname = (char_u *)"values";
1675
1676 type_T *type = get_type_ptr(type_list_gap);
1677 if (type == NULL)
1678 goto done;
1679
1680 type->tt_type = VAR_LIST;
1681 type->tt_member = get_type_ptr(type_list_gap);
1682 if (type->tt_member != NULL)
1683 {
1684 type->tt_member->tt_type = VAR_OBJECT;
1685 type->tt_member->tt_class = en;
1686 }
1687
1688 rc = add_member(gap, varname, varname + 6, FALSE, FALSE, TRUE, TRUE, type,
1689 vim_strsave((char_u *)fga.ga_data));
1690
1691done:
1692 vim_free(fga.ga_data);
1693
1694 return rc;
1695}
1696
1697/*
1698 * Clear the constructor method names in a enum class, so that an enum class
1699 * cannot be instantiated.
1700 */
1701 static void
1702enum_clear_constructors(class_T *en)
1703{
1704 for (int i = 0; i < en->class_class_function_count; ++i)
1705 {
1706 ufunc_T *fp = en->class_class_functions[i];
1707
1708 if (fp->uf_flags & FC_NEW)
1709 *fp->uf_name = NUL;
1710 }
1711}
1712
1713/*
1714 * Initialize the name and ordinal object variable in the enum value "enval" in
1715 * the enum "en". These values are set during the enum value object creation.
1716 */
1717 void
1718enum_set_internal_obj_vars(class_T *en, object_T *enval)
1719{
1720 int i;
1721
1722 for (i = 0; i < en->class_class_member_count; ++i)
1723 {
1724 typval_T *en_tv = en->class_members_tv + i;
1725 if (en_tv != NULL && en_tv->v_type == VAR_UNKNOWN)
1726 break;
1727 }
1728
1729 // First object variable is the name
1730 ocmember_T *value_ocm = en->class_class_members + i;
1731 typval_T *name_tv = (typval_T *)(enval + 1);
1732 name_tv->v_type = VAR_STRING;
1733 name_tv->vval.v_string = vim_strsave(value_ocm->ocm_name);
1734
1735 // Second object variable is the ordinal
1736 typval_T *ord_tv = (typval_T *)(name_tv + 1);
1737 ord_tv->v_type = VAR_NUMBER;
1738 ord_tv->vval.v_number = i;
1739}
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001740
1741/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001742 * Handle ":class" and ":abstract class" up to ":endclass".
h-eastaa979c72025-01-03 10:19:45 +01001743 * Handle ":enum" up to ":endenum".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001744 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001745 */
1746 void
1747ex_class(exarg_T *eap)
1748{
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001749 int is_class = eap->cmdidx == CMD_class;
1750 int is_abstract = eap->cmdidx == CMD_abstract;
1751 int is_enum = eap->cmdidx == CMD_enum;
1752 int is_interface;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001753 long start_lnum = SOURCING_LNUM;
1754 char_u *arg = eap->arg;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001755
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001756 if (is_abstract)
1757 {
1758 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1759 {
1760 semsg(_(e_invalid_argument_str), arg);
1761 return;
1762 }
1763 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001764 is_class = TRUE;
1765 }
1766
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001767 is_interface = !is_class && !is_enum;
1768
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001769 if (!current_script_is_vim9()
1770 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001771 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001772 {
1773 if (is_class)
1774 emsg(_(e_class_can_only_be_defined_in_vim9_script));
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001775 else if (is_enum)
1776 emsg(_(e_enum_can_only_be_defined_in_vim9_script));
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001777 else
1778 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1779 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001780 }
1781
1782 if (!ASCII_ISUPPER(*arg))
1783 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001784 if (is_class)
1785 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001786 else if (is_enum)
1787 semsg(_(e_enum_name_must_start_with_uppercase_letter_str), arg);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001788 else
1789 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1790 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001791 return;
1792 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001793 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1794 if (!IS_WHITE_OR_NUL(*name_end))
1795 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001796 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001797 return;
1798 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001799 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001800
1801 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001802 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001803
Bram Moolenaar83677162023-01-08 19:54:10 +00001804 // Name for "extends BaseClass"
1805 char_u *extends = NULL;
1806
Bram Moolenaar94674f22023-01-06 18:42:20 +00001807 // Names for "implements SomeInterface"
1808 garray_T ga_impl;
1809 ga_init2(&ga_impl, sizeof(char_u *), 5);
1810
1811 arg = skipwhite(name_end);
1812 while (*arg != NUL && *arg != '#' && *arg != '\n')
1813 {
1814 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001815 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001816 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1817 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001818 if (is_enum)
1819 {
1820 emsg(_(e_enum_cannot_extend_class));
1821 goto early_ret;
1822 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001823 if (extends != NULL)
1824 {
1825 emsg(_(e_duplicate_extends));
1826 goto early_ret;
1827 }
1828 arg = skipwhite(arg + 7);
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001829
1830 char_u *end = find_class_name_end(arg);
Bram Moolenaar83677162023-01-08 19:54:10 +00001831 if (!IS_WHITE_OR_NUL(*end))
1832 {
1833 semsg(_(e_white_space_required_after_name_str), arg);
1834 goto early_ret;
1835 }
1836 extends = vim_strnsave(arg, end - arg);
1837 if (extends == NULL)
1838 goto early_ret;
1839
1840 arg = skipwhite(end + 1);
1841 }
1842 else if (STRNCMP(arg, "implements", 10) == 0
1843 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001844 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001845 if (is_interface)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001846 {
1847 emsg(_(e_interface_cannot_use_implements));
1848 goto early_ret;
1849 }
1850
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001851 if (ga_impl.ga_len > 0)
1852 {
1853 emsg(_(e_duplicate_implements));
1854 goto early_ret;
1855 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001856 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001857
1858 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001859 {
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001860 char_u *impl_end = find_class_name_end(arg);
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001861 if ((!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1862 || (*impl_end == ','
1863 && !IS_WHITE_OR_NUL(*(impl_end + 1))))
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001864 {
1865 semsg(_(e_white_space_required_after_name_str), arg);
1866 goto early_ret;
1867 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001868 if (impl_end - arg == 0)
1869 {
1870 emsg(_(e_missing_name_after_implements));
1871 goto early_ret;
1872 }
1873
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001874 char_u *iname = vim_strnsave(arg, impl_end - arg);
1875 if (iname == NULL)
1876 goto early_ret;
1877 for (int i = 0; i < ga_impl.ga_len; ++i)
1878 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1879 {
1880 semsg(_(e_duplicate_interface_after_implements_str),
1881 iname);
1882 vim_free(iname);
1883 goto early_ret;
1884 }
1885 if (ga_add_string(&ga_impl, iname) == FAIL)
1886 {
1887 vim_free(iname);
1888 goto early_ret;
1889 }
1890 if (*impl_end != ',')
1891 {
1892 arg = skipwhite(impl_end);
1893 break;
1894 }
1895 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001896 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001897 }
1898 else
1899 {
1900 semsg(_(e_trailing_characters_str), arg);
1901early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001902 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001903 ga_clear_strings(&ga_impl);
1904 return;
1905 }
1906 }
1907
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001908 garray_T type_list; // list of pointers to allocated types
1909 ga_init2(&type_list, sizeof(type_T *), 10);
1910
Bram Moolenaard505d172022-12-18 21:42:55 +00001911 // Growarray with class members declared in the class.
1912 garray_T classmembers;
1913 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1914
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001915 // Growarray with functions declared in the class.
1916 garray_T classfunctions;
1917 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001918
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001919 // Growarray with object members declared in the class.
1920 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001921 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001922
1923 // Growarray with object methods declared in the class.
1924 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001925 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001926
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01001927 class_T *cl = NULL;
1928 class_T *extends_cl = NULL; // class from "extends" argument
1929 class_T **intf_classes = NULL;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001930 int num_enum_values = 0;
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01001931
1932 cl = ALLOC_CLEAR_ONE(class_T);
1933 if (cl == NULL)
1934 goto cleanup;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001935
1936 if (is_enum)
1937 cl->class_flags = CLASS_ENUM;
1938 else if (is_interface)
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01001939 cl->class_flags = CLASS_INTERFACE;
1940 else if (is_abstract)
1941 cl->class_flags = CLASS_ABSTRACT;
1942
1943 cl->class_refcount = 1;
1944 cl->class_name = vim_strnsave(name_start, name_end - name_start);
1945 if (cl->class_name == NULL)
1946 goto cleanup;
1947
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001948 cl->class_type.tt_type = VAR_CLASS;
1949 cl->class_type.tt_class = cl;
1950 cl->class_object_type.tt_type = VAR_OBJECT;
1951 cl->class_object_type.tt_class = cl;
1952
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01001953 // Add the class to the script-local variables.
1954 // TODO: handle other context, e.g. in a function
1955 // TODO: does uf_hash need to be cleared?
1956 typval_T tv;
1957 tv.v_type = VAR_CLASS;
1958 tv.vval.v_class = cl;
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01001959 SOURCING_LNUM = start_lnum;
1960 int rc = set_var_const(cl->class_name, current_sctx.sc_sid,
1961 NULL, &tv, FALSE, 0, 0);
1962 if (rc == FAIL)
1963 goto cleanup;
1964
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001965 if (is_enum)
1966 {
1967 // All the enum classes have the name and ordinal object variables.
1968 char_u *varname = (char_u *)"name";
1969 if (add_member(&objmembers, varname, varname + 4, FALSE, FALSE, TRUE,
1970 TRUE, &t_string, NULL) == FAIL)
1971 goto cleanup;
1972
1973 varname = (char_u *)"ordinal";
1974 if (add_member(&objmembers, varname, varname + 7, FALSE, FALSE, TRUE,
1975 TRUE, &t_number, NULL) == FAIL)
1976 goto cleanup;
1977 }
1978
1979 // "export class" gets used when creating the class, don't use "is_export"
1980 // for the items inside the class.
1981 is_export = FALSE;
1982
1983 // When parsing an enum definition, this denotes whether all the enumerated
1984 // values are parsed or not.
1985 int enum_end = FALSE;
1986
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001987 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001988 * Go over the body of the class/interface until "endclass" or
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001989 * "endinterface" or "endenum" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001990 */
1991 char_u *theline = NULL;
1992 int success = FALSE;
1993 for (;;)
1994 {
1995 vim_free(theline);
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001996 theline = eap->ea_getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001997 if (theline == NULL)
1998 break;
1999 char_u *line = skipwhite(theline);
2000
Bram Moolenaar418b5472022-12-20 13:38:22 +00002001 // Skip empty and comment lines.
2002 if (*line == NUL)
2003 continue;
2004 if (*line == '#')
2005 {
2006 if (vim9_bad_comment(line))
2007 break;
2008 continue;
2009 }
2010
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002011 char_u *p = line;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002012
2013 char *end_name;
2014 int shortlen;
2015 int fullen;
2016 if (is_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002017 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002018 end_name = "endclass";
2019 shortlen = 4;
2020 fullen = 8;
2021 }
2022 else if (is_enum)
2023 {
2024 end_name = "endenum";
2025 shortlen = 4;
2026 fullen = 7;
2027 }
2028 else
2029 {
2030 end_name = "endinterface";
2031 shortlen = 5;
2032 fullen = 12;
2033 }
2034
2035 if (checkforcmd(&p, end_name, shortlen))
2036 {
2037 if (STRNCMP(line, end_name, fullen) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002038 semsg(_(e_command_cannot_be_shortened_str), line);
2039 else if (*p == '|' || !ends_excmd2(line, p))
2040 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00002041 else
2042 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002043 break;
2044 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002045
2046 int wrong_endname = FALSE;
2047 if (is_class)
2048 wrong_endname = checkforcmd(&p, "endinterface", 5)
2049 || checkforcmd(&p, "endenum", 4);
2050 else if (is_enum)
2051 wrong_endname = checkforcmd(&p, "endclass", 4)
2052 || checkforcmd(&p, "endinterface", 5);
2053 else
2054 wrong_endname = checkforcmd(&p, "endclass", 4)
2055 || checkforcmd(&p, "endenum", 4);
2056 if (wrong_endname)
Bram Moolenaar554d0312023-01-05 19:59:18 +00002057 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00002058 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00002059 break;
2060 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002061
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002062 if (is_enum && !enum_end)
2063 {
2064 // In an enum, all the enumerated values are at the beginning
2065 // separated by comma. The class and object variables/methods
2066 // follow the values.
2067 if (enum_parse_values(eap, cl, line, &classmembers,
2068 &num_enum_values, &enum_end) == FAIL)
2069 break;
Yegappan Lakshmananabedca92024-03-29 10:08:23 +01002070
2071 if (enum_end)
2072 // Add the enum "values" class variable.
2073 enum_add_values_member(cl, &classmembers, num_enum_values,
2074 &type_list);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002075 continue;
2076 }
2077
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002078 int has_public = FALSE;
2079 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002080 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002081 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002082 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002083 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002084 break;
2085 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002086 if (is_interface)
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002087 {
Yegappan Lakshmananb90e3bc2023-09-28 23:06:48 +02002088 emsg(_(e_public_variable_not_supported_in_interface));
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002089 break;
2090 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002091 has_public = TRUE;
2092 p = skipwhite(line + 6);
2093
Yegappan Lakshmananc51578f2024-04-13 17:58:09 +02002094 if (STRNCMP(p, "def", 3) == 0)
2095 {
2096 emsg(_(e_public_keyword_not_supported_for_method));
2097 break;
2098 }
2099
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002100 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "static", 6) != 0
2101 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002102 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002103 emsg(_(e_public_must_be_followed_by_var_static_final_or_const));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002104 break;
2105 }
2106 }
Bram Moolenaard505d172022-12-18 21:42:55 +00002107
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002108 int abstract_method = FALSE;
2109 char_u *pa = p;
2110 if (checkforcmd(&p, "abstract", 3))
2111 {
2112 if (STRNCMP(pa, "abstract", 8) != 0)
2113 {
2114 semsg(_(e_command_cannot_be_shortened_str), pa);
2115 break;
2116 }
2117
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002118 if (is_enum)
2119 {
2120 // "abstract" not supported in an enum
2121 emsg(_(e_abstract_cannot_be_used_in_enum));
2122 break;
2123 }
2124
2125 if (is_interface)
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01002126 {
2127 // "abstract" not supported in an interface
2128 emsg(_(e_abstract_cannot_be_used_in_interface));
2129 break;
2130 }
2131
2132 if (!is_abstract)
2133 {
2134 semsg(_(e_abstract_method_in_concrete_class), pa);
2135 break;
2136 }
2137
Yegappan Lakshmanan5a539252023-11-04 09:42:46 +01002138 p = skipwhite(pa + 8);
2139 if (STRNCMP(p, "def", 3) != 0)
2140 {
2141 emsg(_(e_abstract_must_be_followed_by_def));
2142 break;
2143 }
2144
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01002145 abstract_method = TRUE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002146 }
2147
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002148 int has_static = FALSE;
2149 char_u *ps = p;
2150 if (checkforcmd(&p, "static", 4))
2151 {
2152 if (STRNCMP(ps, "static", 6) != 0)
2153 {
2154 semsg(_(e_command_cannot_be_shortened_str), ps);
2155 break;
2156 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002157
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002158 if (is_interface)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002159 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002160 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002161 break;
2162 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002163 has_static = TRUE;
2164 p = skipwhite(ps + 6);
Doug Kearns74da0ee2023-12-14 20:26:26 +01002165
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002166 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "def", 3) != 0
2167 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Doug Kearns74da0ee2023-12-14 20:26:26 +01002168 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002169 emsg(_(e_static_must_be_followed_by_var_def_final_or_const));
Doug Kearns74da0ee2023-12-14 20:26:26 +01002170 break;
2171 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002172 }
2173
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002174 int has_final = FALSE;
2175 int has_var = FALSE;
2176 int has_const = FALSE;
2177 if (checkforcmd(&p, "var", 3))
2178 has_var = TRUE;
2179 else if (checkforcmd(&p, "final", 5))
2180 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002181 if (is_interface)
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002182 {
2183 emsg(_(e_final_variable_not_supported_in_interface));
2184 break;
2185 }
2186 has_final = TRUE;
2187 }
2188 else if (checkforcmd(&p, "const", 5))
2189 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002190 if (is_interface)
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002191 {
2192 emsg(_(e_const_variable_not_supported_in_interface));
2193 break;
2194 }
2195 has_const = TRUE;
2196 }
2197 p = skipwhite(p);
2198
Bram Moolenaard505d172022-12-18 21:42:55 +00002199 // object members (public, read access, private):
Doug Kearns74da0ee2023-12-14 20:26:26 +01002200 // "var _varname"
2201 // "var varname"
2202 // "public var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002203 // "final _varname"
2204 // "final varname"
2205 // "public final varname"
2206 // "const _varname"
2207 // "const varname"
2208 // "public const varname"
Doug Kearns74da0ee2023-12-14 20:26:26 +01002209 // class members (public, read access, private):
2210 // "static var _varname"
2211 // "static var varname"
2212 // "public static var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002213 // "static final _varname"
2214 // "static final varname"
2215 // "public static final varname"
2216 // "static const _varname"
2217 // "static const varname"
2218 // "public static const varname"
2219 if (has_var || has_final || has_const)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002220 {
Doug Kearns74da0ee2023-12-14 20:26:26 +01002221 char_u *varname = p;
Bram Moolenaard505d172022-12-18 21:42:55 +00002222 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00002223 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00002224 char_u *init_expr = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002225 int has_type = FALSE;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002226
Doug Kearns74da0ee2023-12-14 20:26:26 +01002227 if (!eval_isnamec1(*p))
2228 {
2229 if (has_static)
2230 semsg(_(e_invalid_class_variable_declaration_str), line);
2231 else
2232 semsg(_(e_invalid_object_variable_declaration_str), line);
2233 break;
2234 }
2235
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002236 if (is_interface && *varname == '_')
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002237 {
2238 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01002239 semsg(_(e_protected_variable_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002240 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002241 break;
2242 }
2243
Bram Moolenaard505d172022-12-18 21:42:55 +00002244 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002245 &varname_end, &has_type, &type_list, &type,
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002246 !is_interface ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002247 break;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002248
2249 if (is_reserved_varname(varname, varname_end)
2250 || is_duplicate_variable(&classmembers, &objmembers,
2251 varname, varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02002252 {
2253 vim_free(init_expr);
2254 break;
2255 }
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002256 if (add_member(has_static ? &classmembers : &objmembers, varname,
2257 varname_end, has_public, has_final, has_const,
2258 has_type, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00002259 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002260 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002261 break;
2262 }
Bram Moolenaard505d172022-12-18 21:42:55 +00002263 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002264
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002265 // constructors:
2266 // def new()
2267 // enddef
2268 // def newOther()
2269 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002270 // object methods and class functions:
2271 // def SomeMethod()
2272 // enddef
2273 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002274 // enddef
2275 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002276 // def <Tval> someMethod()
2277 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002278 else if (checkforcmd(&p, "def", 3))
2279 {
2280 exarg_T ea;
2281 garray_T lines_to_free;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002282 int is_new = STRNCMP(p, "new", 3) == 0;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002283
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002284 if (has_public)
2285 {
2286 // "public" keyword is not supported when defining an object or
2287 // class method
2288 emsg(_(e_public_keyword_not_supported_for_method));
2289 break;
2290 }
2291
2292 if (*p == NUL)
2293 {
2294 // No method name following def
2295 semsg(_(e_not_valid_command_in_class_str), line);
2296 break;
2297 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002298
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002299 if (is_interface && *p == '_')
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01002300 {
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002301 // private methods are not supported in an interface
2302 semsg(_(e_protected_method_not_supported_in_interface), p);
2303 break;
2304 }
2305
2306 if (has_static && !is_new && SAFE_islower(*p) &&
2307 is_valid_builtin_obj_methodname(p))
2308 {
2309 semsg(_(e_builtin_class_method_not_supported), p);
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01002310 break;
2311 }
2312
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002313 CLEAR_FIELD(ea);
2314 ea.cmd = line;
2315 ea.arg = p;
2316 ea.cmdidx = CMD_def;
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002317 ea.ea_getline = eap->ea_getline;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002318 ea.cookie = eap->cookie;
2319
2320 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002321 int class_flags;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002322 if (is_interface)
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002323 class_flags = CF_INTERFACE;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002324 else
2325 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
Bram Moolenaar554d0312023-01-05 19:59:18 +00002326 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02002327 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002328 ga_clear_strings(&lines_to_free);
2329
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002330 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002331 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002332 char_u *name = uf->uf_name;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02002333
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002334 if (is_new && !is_valid_constructor(uf, is_abstract,
2335 has_static))
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002336 {
2337 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01002338 semsg(_(e_protected_method_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002339 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002340 func_clear_free(uf, FALSE);
2341 break;
2342 }
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002343
2344 // check for builtin method
2345 if (!is_new && SAFE_islower(*name) &&
2346 !object_check_builtin_method_sig(uf))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00002347 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02002348 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00002349 break;
2350 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02002351
Bram Moolenaar58b40092023-01-11 15:59:05 +00002352 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002353 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002354 {
2355 success = FALSE;
2356 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02002357 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002358 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00002359
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002360 garray_T *fgap = has_static || is_new
2361 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002362 if (ga_grow(fgap, 1) == OK)
2363 {
2364 if (is_new)
2365 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002366
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002367 if (abstract_method)
2368 uf->uf_flags |= FC_ABSTRACT;
2369
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002370 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
2371 ++fgap->ga_len;
2372 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002373 }
2374 }
2375
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002376 else
2377 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00002378 if (is_class)
2379 semsg(_(e_not_valid_command_in_class_str), line);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002380 else if (is_enum)
2381 semsg(_(e_not_valid_command_in_enum_str), line);
Bram Moolenaar554d0312023-01-05 19:59:18 +00002382 else
2383 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002384 break;
2385 }
2386 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002387
2388 if (theline == NULL && !success && is_enum)
2389 emsg(_(e_missing_endenum));
2390
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002391 vim_free(theline);
2392
Yegappan Lakshmananabedca92024-03-29 10:08:23 +01002393 if (success && is_enum && num_enum_values == 0)
2394 // Empty enum statement. Add an empty "values" class variable
2395 enum_add_values_member(cl, &classmembers, 0, &type_list);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002396
Bram Moolenaar83677162023-01-08 19:54:10 +00002397 /*
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002398 * Check a few things
Bram Moolenaar83677162023-01-08 19:54:10 +00002399 */
2400
2401 // Check the "extends" class is valid.
2402 if (success && extends != NULL)
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002403 success = validate_extends_class(cl, extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00002404 VIM_CLEAR(extends);
2405
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002406 // Check the new object methods to make sure their access (public or
2407 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002408 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002409 success = validate_extends_methods(&objmethods, extends_cl);
2410
2411 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002412 // variables in the extended class lineage. If an interface is extending
2413 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002414 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002415 {
2416 if (is_class)
2417 success = extends_check_dup_members(&objmembers, extends_cl);
2418 else
2419 success = extends_check_intf_var_type(&objmembers, extends_cl);
2420 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002421
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002422 // When extending an abstract class, make sure all the abstract methods in
2423 // the parent class are implemented. If the current class is an abstract
2424 // class, then there is no need for this check.
2425 if (success && !is_abstract && extends_cl != NULL
2426 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002427 success = validate_abstract_class_methods(&classfunctions,
2428 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002429
Bram Moolenaar83677162023-01-08 19:54:10 +00002430 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00002431 if (success && ga_impl.ga_len > 0)
2432 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002433 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
2434
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002435 success = validate_implements_classes(&ga_impl, intf_classes,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002436 &objmethods, &objmembers, extends_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00002437 }
2438
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002439 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00002440 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002441 success = check_func_arg_names(&classfunctions, &objmethods,
2442 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00002443
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002444 if (success)
2445 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002446 // "endclass" or "endinterface" or "endenum" encountered without any
2447 // failures
Bram Moolenaard505d172022-12-18 21:42:55 +00002448
Bram Moolenaard0200c82023-01-28 15:19:40 +00002449 if (extends_cl != NULL)
2450 {
2451 cl->class_extends = extends_cl;
2452 extends_cl->class_flags |= CLASS_EXTENDED;
2453 }
Bram Moolenaar83677162023-01-08 19:54:10 +00002454
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002455 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00002456 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002457 NULL,
2458 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00002459 &cl->class_class_members,
2460 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00002461 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00002462 extends_cl == NULL ? NULL
2463 : extends_cl->class_obj_members,
2464 extends_cl == NULL ? 0
2465 : extends_cl->class_obj_member_count,
2466 &cl->class_obj_members,
2467 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002468 goto cleanup;
2469
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00002470 if (ga_impl.ga_len > 0)
2471 {
2472 // Move the "implements" names into the class.
2473 cl->class_interface_count = ga_impl.ga_len;
2474 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
2475 if (cl->class_interfaces == NULL)
2476 goto cleanup;
2477 for (int i = 0; i < ga_impl.ga_len; ++i)
2478 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
2479 VIM_CLEAR(ga_impl.ga_data);
2480 ga_impl.ga_len = 0;
2481
Bram Moolenaard0200c82023-01-28 15:19:40 +00002482 cl->class_interfaces_cl = intf_classes;
2483 intf_classes = NULL;
2484 }
2485
2486 if (cl->class_interface_count > 0 || extends_cl != NULL)
2487 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002488 // Add a method and member lookup table to each of the interface
2489 // classes.
2490 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
2491 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00002492 }
2493
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002494 int have_new = FALSE;
2495 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002496 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002497 {
2498 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
2499 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002500 {
2501 have_new = TRUE;
2502 break;
2503 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002504 }
2505
2506 if (have_new)
2507 // The return type of new() is an object of class "cl"
2508 class_func->uf_ret_type->tt_class = cl;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002509 else if ((is_class || is_enum) && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002510 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002511 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002512
Bram Moolenaar58b40092023-01-11 15:59:05 +00002513 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002514 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
2515 &objmethods) == FAIL)
2516 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002517
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002518 update_builtin_method_index(cl);
2519
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002520 class_created(cl);
2521
2522 // Allocate a typval for each class member and initialize it.
2523 if ((is_class || is_enum) && cl->class_class_member_count > 0)
2524 if (add_class_members(cl, eap, &type_list) == FAIL)
2525 goto cleanup;
2526
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002527 cl->class_type_list = type_list;
2528
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002529 if (is_enum)
2530 {
2531 // clear the constructor method names, so that an enum class cannot
2532 // be instantiated
2533 enum_clear_constructors(cl);
2534 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002535
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002536 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00002537 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002538
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002539 return;
2540 }
2541
2542cleanup:
Bram Moolenaar83677162023-01-08 19:54:10 +00002543 vim_free(extends);
2544 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002545
2546 if (intf_classes != NULL)
2547 {
2548 for (int i = 0; i < ga_impl.ga_len; ++i)
2549 class_unref(intf_classes[i]);
2550 vim_free(intf_classes);
2551 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00002552 ga_clear_strings(&ga_impl);
2553
Bram Moolenaard505d172022-12-18 21:42:55 +00002554 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002555 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002556 garray_T *gap = round == 1 ? &classmembers : &objmembers;
2557 if (gap->ga_len == 0 || gap->ga_data == NULL)
2558 continue;
2559
2560 for (int i = 0; i < gap->ga_len; ++i)
2561 {
2562 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2563 vim_free(m->ocm_name);
2564 vim_free(m->ocm_init);
2565 }
2566 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002567 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002568
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002569 for (int i = 0; i < objmethods.ga_len; ++i)
2570 {
2571 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2572 func_clear_free(uf, FALSE);
2573 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002574 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002575
2576 for (int i = 0; i < classfunctions.ga_len; ++i)
2577 {
2578 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2579 func_clear_free(uf, FALSE);
2580 }
2581 ga_clear(&classfunctions);
2582
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002583 clear_type_list(&type_list);
2584}
2585
2586/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002587 * Find member "name" in class "cl", set "member_idx" to the member index and
2588 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002589 * When "is_object" is TRUE, then look for object members. Otherwise look for
2590 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002591 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002592 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002593 */
2594 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002595oc_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002596 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002597 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002598 char_u *name,
2599 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002600 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002601{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002602 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002603 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002604
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002605 *member_idx = -1; // not found (yet)
2606
2607 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2608 member_idx);
2609 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002610 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002611 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2612 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002613 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002614 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002615
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002616 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002617}
2618
2619/*
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002620 * Given a class or object variable index, return the variable type
2621 */
2622 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002623oc_member_type_by_idx(
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002624 class_T *cl,
2625 int is_object,
2626 int member_idx)
2627{
2628 ocmember_T *m;
2629 int member_count;
2630
2631 if (is_object)
2632 {
2633 m = cl->class_obj_members;
2634 member_count = cl->class_obj_member_count;
2635 }
2636 else
2637 {
2638 m = cl->class_class_members;
2639 member_count = cl->class_class_member_count;
2640 }
2641
2642 if (member_idx >= member_count)
2643 return NULL;
2644
2645 return m[member_idx].ocm_type;
2646}
2647
2648/*
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002649 * Type aliases (:type)
2650 */
2651
Yegappan Lakshmanana04003a2024-10-27 21:54:11 +01002652 static void
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002653typealias_free(typealias_T *ta)
2654{
2655 // ta->ta_type is freed in clear_type_list()
2656 vim_free(ta->ta_name);
2657 vim_free(ta);
2658}
2659
2660 void
2661typealias_unref(typealias_T *ta)
2662{
2663 if (ta != NULL && --ta->ta_refcount <= 0)
2664 typealias_free(ta);
2665}
2666
2667/*
2668 * Handle ":type". Create an alias for a type specification.
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002669 */
2670 void
Dominique Pellé0268ff32024-07-28 21:12:20 +02002671ex_type(exarg_T *eap)
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002672{
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002673 char_u *arg = eap->arg;
2674
2675 if (!current_script_is_vim9()
2676 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002677 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002678 {
2679 emsg(_(e_type_can_only_be_defined_in_vim9_script));
2680 return;
2681 }
2682
2683 if (*arg == NUL)
2684 {
2685 emsg(_(e_missing_typealias_name));
2686 return;
2687 }
2688
2689 if (!ASCII_ISUPPER(*arg))
2690 {
2691 semsg(_(e_type_name_must_start_with_uppercase_letter_str), arg);
2692 return;
2693 }
2694
2695 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
2696 if (!IS_WHITE_OR_NUL(*name_end))
2697 {
2698 semsg(_(e_white_space_required_after_name_str), arg);
2699 return;
2700 }
2701 char_u *name_start = arg;
2702
2703 arg = skipwhite(name_end);
2704 if (*arg != '=')
2705 {
2706 semsg(_(e_missing_equal_str), arg);
2707 return;
2708 }
2709 if (!IS_WHITE_OR_NUL(*(arg + 1)))
2710 {
2711 semsg(_(e_white_space_required_after_str_str), "=", arg);
2712 return;
2713 }
2714 arg++;
2715 arg = skipwhite(arg);
2716
2717 if (*arg == NUL)
2718 {
2719 emsg(_(e_missing_typealias_type));
2720 return;
2721 }
2722
2723 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
2724 type_T *type = parse_type(&arg, &si->sn_type_list, TRUE);
2725 if (type == NULL)
2726 return;
2727
2728 if (*arg != NUL)
2729 {
2730 // some text after the type
2731 semsg(_(e_trailing_characters_str), arg);
2732 return;
2733 }
2734
2735 int cc = *name_end;
2736 *name_end = NUL;
2737
2738 typval_T tv;
2739 tv.v_type = VAR_UNKNOWN;
2740 if (eval_variable_import(name_start, &tv) == OK)
2741 {
2742 if (tv.v_type == VAR_TYPEALIAS)
2743 semsg(_(e_typealias_already_exists_for_str), name_start);
2744 else
2745 semsg(_(e_redefining_script_item_str), name_start);
2746 clear_tv(&tv);
2747 goto done;
2748 }
2749
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02002750 // Create a script-local variable for the type alias.
2751 if (type->tt_type != VAR_OBJECT)
2752 {
2753 tv.v_type = VAR_TYPEALIAS;
2754 tv.v_lock = 0;
2755 tv.vval.v_typealias = ALLOC_CLEAR_ONE(typealias_T);
2756 ++tv.vval.v_typealias->ta_refcount;
2757 tv.vval.v_typealias->ta_name = vim_strsave(name_start);
2758 tv.vval.v_typealias->ta_type = type;
2759 }
2760 else
2761 {
2762 // When creating a type alias for a class, use the class type itself to
2763 // create the type alias variable. This is needed to use the type
2764 // alias to invoke class methods (e.g. new()) and use class variables.
2765 tv.v_type = VAR_CLASS;
2766 tv.v_lock = 0;
2767 tv.vval.v_class = type->tt_class;
2768 ++tv.vval.v_class->class_refcount;
2769 }
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002770 set_var_const(name_start, current_sctx.sc_sid, NULL, &tv, FALSE,
2771 ASSIGN_CONST | ASSIGN_FINAL, 0);
2772
2773done:
2774 *name_end = cc;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002775}
2776
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002777/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002778 * Returns OK if a member variable named "name" is present in the class "cl".
2779 * Otherwise returns FAIL. If found, the member variable typval is set in
2780 * "rettv". If "is_object" is TRUE, then the object member variable table is
2781 * searched. Otherwise the class member variable table is searched.
2782 */
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002783 int
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002784get_member_tv(
2785 class_T *cl,
2786 int is_object,
2787 char_u *name,
2788 size_t namelen,
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002789 class_T *current_class,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002790 typval_T *rettv)
2791{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002792 ocmember_T *m;
2793 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002794
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002795 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2796 &m_idx);
2797 if (m == NULL)
2798 return FAIL;
2799
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002800 if (*name == '_' && (current_class == NULL ||
2801 !class_instance_of(current_class, cl)))
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002802 {
Ernie Rael03042a22023-11-11 08:53:32 +01002803 emsg_var_cl_define(e_cannot_access_protected_variable_str,
Ernie Raele6c9aa52023-10-06 19:55:52 +02002804 m->ocm_name, 0, cl);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002805 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002806 }
2807
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002808 if (is_object)
2809 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002810 // The object only contains a pointer to the class, the member values
2811 // array follows right after that.
2812 object_T *obj = rettv->vval.v_object;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002813 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2814 copy_tv(tv, rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002815 object_unref(obj);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002816 }
2817 else
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002818 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002819 copy_tv(&cl->class_members_tv[m_idx], rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002820 class_unref(cl);
2821 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002822
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002823 return OK;
2824}
2825
2826/*
2827 * Call an object or class method "name" in class "cl". The method return
2828 * value is returned in "rettv".
2829 */
2830 static int
2831call_oc_method(
2832 class_T *cl,
2833 char_u *name,
2834 size_t len,
2835 char_u *name_end,
2836 evalarg_T *evalarg,
2837 char_u **arg,
2838 typval_T *rettv)
2839{
2840 ufunc_T *fp;
2841 typval_T argvars[MAX_FUNC_ARGS + 1];
2842 int argcount = 0;
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002843 ocmember_T *ocm = NULL;
2844 int m_idx;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002845
2846 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
2847 if (fp == NULL)
2848 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002849 // could be an object or class funcref variable
2850 ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
2851 if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
2852 {
2853 method_not_found_msg(cl, rettv->v_type, name, len);
2854 return FAIL;
2855 }
2856
Yegappan Lakshmanan3e336502024-04-04 19:35:59 +02002857 if (*name == '_')
2858 {
2859 // Protected object or class funcref variable
2860 semsg(_(e_cannot_access_protected_variable_str), ocm->ocm_name,
2861 cl->class_name);
2862 return FAIL;
2863 }
2864
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002865 if (rettv->v_type == VAR_OBJECT)
2866 {
2867 // funcref object variable
2868 object_T *obj = rettv->vval.v_object;
2869 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2870 copy_tv(tv, rettv);
2871 }
2872 else
2873 // funcref class variable
2874 copy_tv(&cl->class_members_tv[m_idx], rettv);
2875 *arg = name_end;
2876 return OK;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002877 }
2878
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002879 if (ocm == NULL && *fp->uf_name == '_')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002880 {
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002881 // Cannot access a protected method outside of a class
Ernie Rael03042a22023-11-11 08:53:32 +01002882 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002883 return FAIL;
2884 }
2885
2886 char_u *argp = name_end;
Ernie Raelb077b582023-12-14 20:11:44 +01002887 int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount, FALSE);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002888 if (ret == FAIL)
2889 return FAIL;
2890
2891 funcexe_T funcexe;
2892 CLEAR_FIELD(funcexe);
2893 funcexe.fe_evaluate = TRUE;
2894 if (rettv->v_type == VAR_OBJECT)
2895 {
2896 funcexe.fe_object = rettv->vval.v_object;
2897 ++funcexe.fe_object->obj_refcount;
2898 }
2899
2900 // Clear the class or object after calling the function, in
2901 // case the refcount is one.
2902 typval_T tv_tofree = *rettv;
2903 rettv->v_type = VAR_UNKNOWN;
2904
2905 // Call the user function. Result goes into rettv;
2906 int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
2907 NULL);
2908
2909 // Clear the previous rettv and the arguments.
2910 clear_tv(&tv_tofree);
2911 for (int idx = 0; idx < argcount; ++idx)
2912 clear_tv(&argvars[idx]);
2913
2914 if (error != FCERR_NONE)
2915 {
2916 user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
2917 return FAIL;
2918 }
2919 *arg = argp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002920
2921 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002922}
2923
2924/*
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01002925 * Create a partial typval for "obj.obj_method" and store it in "rettv".
2926 * Returns OK on success and FAIL on memory allocation failure.
2927 */
2928 int
2929obj_method_to_partial_tv(object_T *obj, ufunc_T *obj_method, typval_T *rettv)
2930{
2931 partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
2932 if (pt == NULL)
2933 return FAIL;
2934
2935 pt->pt_refcount = 1;
2936 if (obj != NULL)
2937 {
2938 pt->pt_obj = obj;
2939 ++pt->pt_obj->obj_refcount;
2940 }
2941 pt->pt_auto = TRUE;
2942 pt->pt_func = obj_method;
2943 func_ptr_ref(pt->pt_func);
2944
2945 rettv->v_type = VAR_PARTIAL;
2946 rettv->vval.v_partial = pt;
2947
2948 return OK;
2949}
2950
2951/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002952 * Evaluate what comes after a class:
2953 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002954 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002955 * - class constructor: SomeClass.new()
2956 * - object member: someObject.varname
2957 * - object method: someObject.SomeMethod()
2958 *
2959 * "*arg" points to the '.'.
2960 * "*arg" is advanced to after the member name or method call.
2961 *
2962 * Returns FAIL or OK.
2963 */
2964 int
2965class_object_index(
2966 char_u **arg,
2967 typval_T *rettv,
2968 evalarg_T *evalarg,
2969 int verbose UNUSED) // give error messages
2970{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002971 if (VIM_ISWHITE((*arg)[1]))
2972 {
2973 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
2974 return FAIL;
2975 }
2976
2977 ++*arg;
2978 char_u *name = *arg;
2979 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2980 if (name_end == name)
2981 return FAIL;
2982 size_t len = name_end - name;
2983
Ernie Raeld615a312023-10-05 20:28:16 +02002984 int did_emsg_save = did_emsg;
Bram Moolenaar552bdca2023-02-17 21:08:50 +00002985 class_T *cl;
2986 if (rettv->v_type == VAR_CLASS)
2987 cl = rettv->vval.v_class;
2988 else // VAR_OBJECT
2989 {
2990 if (rettv->vval.v_object == NULL)
2991 {
2992 emsg(_(e_using_null_object));
2993 return FAIL;
2994 }
2995 cl = rettv->vval.v_object->obj_class;
2996 }
2997
Bram Moolenaard13dd302023-03-11 20:56:35 +00002998 if (cl == NULL)
2999 {
3000 emsg(_(e_incomplete_type));
3001 return FAIL;
3002 }
3003
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003004 if (*name_end == '(')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003005 // Invoke the class or object method
3006 return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003007
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003008 else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003009 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003010 // Search in the object member variable table and the class member
3011 // variable table.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003012 int is_object = rettv->v_type == VAR_OBJECT;
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003013 if (get_member_tv(cl, is_object, name, len, NULL, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003014 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003015 *arg = name_end;
3016 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003017 }
3018
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003019 // could be a class method or an object method
3020 int fidx;
3021 ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
3022 if (fp != NULL)
3023 {
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003024 // Protected methods are not accessible outside the class
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003025 if (*name == '_')
3026 {
Ernie Rael03042a22023-11-11 08:53:32 +01003027 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003028 return FAIL;
3029 }
3030
Yegappan Lakshmanan56d45f12024-11-11 19:58:55 +01003031 if (obj_method_to_partial_tv(is_object ? rettv->vval.v_object :
3032 NULL, fp, rettv) == FAIL)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003033 return FAIL;
3034
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003035 *arg = name_end;
3036 return OK;
3037 }
3038
Ernie Raeld615a312023-10-05 20:28:16 +02003039 if (did_emsg == did_emsg_save)
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +02003040 member_not_found_msg(cl, rettv->v_type, name, len);
Bram Moolenaard505d172022-12-18 21:42:55 +00003041 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003042
3043 return FAIL;
3044}
3045
3046/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003047 * If "arg" points to a class or object method, return it.
3048 * Otherwise return NULL.
3049 */
3050 ufunc_T *
3051find_class_func(char_u **arg)
3052{
3053 char_u *name = *arg;
3054 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
3055 if (name_end == name || *name_end != '.')
3056 return NULL;
3057
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003058 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003059 size_t len = name_end - name;
3060 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003061 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00003062 if (eval_variable(name, (int)len,
3063 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003064 return NULL;
3065 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00003066 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003067
3068 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
3069 : tv.vval.v_object->obj_class;
3070 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00003071 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003072 char_u *fname = name_end + 1;
3073 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
3074 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00003075 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003076 len = fname_end - fname;
3077
Ernie Rael4d00b832023-09-11 19:54:42 +02003078 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003079
Bram Moolenaareb533502022-12-14 15:06:11 +00003080fail_after_eval:
3081 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003082 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003083}
3084
3085/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003086 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003087 * Returns -1, if the variable is not found.
3088 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003089 */
3090 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003091class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003092{
Ernie Rael4d00b832023-09-11 19:54:42 +02003093 int idx;
3094 class_member_lookup(cl, name, namelen, &idx);
3095 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003096}
3097
3098/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003099 * Returns a pointer to the class member variable "name" in the class "cl".
3100 * Returns NULL if the variable is not found.
3101 * The member variable index is set in "idx".
3102 */
3103 ocmember_T *
3104class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3105{
Ernie Rael4d00b832023-09-11 19:54:42 +02003106 ocmember_T *ret_m = NULL;
3107 int ret_idx = -1;
3108 for (int i = 0; i < cl->class_class_member_count; ++i)
3109 {
3110 ocmember_T *m = &cl->class_class_members[i];
3111 if (namelen)
3112 {
3113 if (STRNCMP(name, m->ocm_name, namelen) == 0
3114 && m->ocm_name[namelen] == NUL)
3115 {
3116 ret_m = m;
3117 ret_idx = i;
3118 break;
3119 }
3120 }
3121 else if (STRCMP(name, m->ocm_name) == 0)
3122 {
3123 ret_m = m;
3124 ret_idx = i;
zeertzjqd9be94c2024-07-14 10:20:20 +02003125 break;
Ernie Rael4d00b832023-09-11 19:54:42 +02003126 }
3127 }
3128 if (idx != NULL)
3129 *idx = ret_idx;
3130 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003131}
3132
3133/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003134 * Returns a pointer to the class method "name" in class "cl".
3135 * Returns NULL if the method is not found.
3136 * The method index is set in "idx".
3137 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003138 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003139class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3140{
Ernie Rael4d00b832023-09-11 19:54:42 +02003141 ufunc_T *ret_fp = NULL;
3142 int ret_idx = -1;
3143 for (int i = 0; i < cl->class_class_function_count; ++i)
3144 {
3145 ufunc_T *fp = cl->class_class_functions[i];
3146 char_u *ufname = (char_u *)fp->uf_name;
3147 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
3148 {
3149 ret_fp = fp;
3150 ret_idx = i;
3151 break;
3152 }
3153 }
3154 if (idx != NULL)
3155 *idx = ret_idx;
3156 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003157}
3158
3159/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003160 * Returns the index of class method "name" in the class "cl".
3161 * Returns -1, if the method is not found.
3162 */
3163 int
3164class_method_idx(class_T *cl, char_u *name, size_t namelen)
3165{
3166 int idx;
3167 class_method_lookup(cl, name, namelen, &idx);
3168 return idx;
3169}
3170
3171/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003172 * Returns the index of object member variable "name" in the class "cl".
3173 * Returns -1, if the variable is not found.
3174 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
3175 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003176 static int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003177object_member_idx(class_T *cl, char_u *name, size_t namelen)
3178{
Ernie Rael4d00b832023-09-11 19:54:42 +02003179 int idx;
3180 object_member_lookup(cl, name, namelen, &idx);
3181 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02003182}
3183
3184/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003185 * Returns a pointer to the object member variable "name" in the class "cl".
3186 * Returns NULL if the variable is not found.
3187 * The object member variable index is set in "idx".
3188 */
3189 ocmember_T *
3190object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3191{
Ernie Rael4d00b832023-09-11 19:54:42 +02003192 ocmember_T *ret_m = NULL;
3193 int ret_idx = -1;
3194 for (int i = 0; i < cl->class_obj_member_count; ++i)
3195 {
3196 ocmember_T *m = &cl->class_obj_members[i];
3197 if (namelen)
3198 {
3199 if (STRNCMP(name, m->ocm_name, namelen) == 0
3200 && m->ocm_name[namelen] == NUL)
3201 {
3202 ret_m = m;
3203 ret_idx = i;
3204 break;
3205 }
3206 }
3207 else if (STRCMP(name, m->ocm_name) == 0)
zeertzjqd9be94c2024-07-14 10:20:20 +02003208 {
Ernie Rael4d00b832023-09-11 19:54:42 +02003209 ret_m = m;
3210 ret_idx = i;
zeertzjqd9be94c2024-07-14 10:20:20 +02003211 break;
3212 }
Ernie Rael4d00b832023-09-11 19:54:42 +02003213 }
3214 if (idx != NULL)
3215 *idx = ret_idx;
3216 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003217}
3218
3219/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003220 * Returns a pointer to the object method "name" in class "cl".
3221 * Returns NULL if the method is not found.
3222 * The object method index is set in "idx".
3223 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003224 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003225object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3226{
Ernie Rael4d00b832023-09-11 19:54:42 +02003227 ufunc_T *ret_fp = NULL;
3228 int ret_idx = -1;
3229 for (int i = 0; i < cl->class_obj_method_count; ++i)
3230 {
3231 ufunc_T *fp = cl->class_obj_methods[i];
3232 // Use a separate pointer to avoid that ASAN complains about
3233 // uf_name[] only being 4 characters.
3234 char_u *ufname = (char_u *)fp->uf_name;
3235 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
3236 {
3237 ret_fp = fp;
3238 ret_idx = i;
3239 break;
3240 }
3241 }
3242 if (idx != NULL)
3243 *idx = ret_idx;
3244 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003245}
3246
3247/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003248 * Returns the index of object method "name" in the class "cl".
3249 * Returns -1, if the method is not found.
3250 */
3251 int
3252object_method_idx(class_T *cl, char_u *name, size_t namelen)
3253{
3254 int idx;
3255 object_method_lookup(cl, name, namelen, &idx);
3256 return idx;
3257}
3258
3259/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003260 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
3261 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
3262 * object member variable.
3263 *
3264 * Returns a pointer to the member variable structure if variable is found.
3265 * Otherwise returns NULL. The member variable index is set in "*idx".
3266 */
3267 ocmember_T *
3268member_lookup(
3269 class_T *cl,
3270 vartype_T v_type,
3271 char_u *name,
3272 size_t namelen,
3273 int *idx)
3274{
3275 if (v_type == VAR_CLASS)
3276 return class_member_lookup(cl, name, namelen, idx);
3277 else
3278 return object_member_lookup(cl, name, namelen, idx);
3279}
3280
3281/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003282 * Find the class that defines the named member. Look up the hierarchy
3283 * starting at "cl".
3284 *
3285 * Return the class that defines the member "name", else NULL.
3286 * Fill in "p_m", if specified, for ocmember_T in found class.
3287 */
3288// NOTE: if useful for something could also indirectly return vartype and idx.
3289 static class_T *
3290class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m)
3291{
3292 class_T *cl_found = NULL;
3293 vartype_T vartype = VAR_UNKNOWN;
3294 ocmember_T *m_found = NULL;
3295
3296 len = len != 0 ? len : STRLEN(name);
3297
3298 // Loop assumes if member is not defined in "cl", then it is not
3299 // defined in any super class; the last class where it's found is the
3300 // class where it is defined. Once the vartype is found, the other
3301 // type is no longer checked.
3302 for (class_T *super = cl; super != NULL; super = super->class_extends)
3303 {
3304 class_T *cl_tmp = NULL;
3305 ocmember_T *m = NULL;
3306 if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT)
3307 {
3308 if ((m = object_member_lookup(super, name, len, NULL)) != NULL)
3309 {
3310 cl_tmp = super;
3311 vartype = VAR_OBJECT;
3312 }
3313 }
3314 if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS)
3315 {
3316 if (( m = class_member_lookup(super, name, len, NULL)) != NULL)
3317 {
3318 cl_tmp = super;
Zdenek Dohnal215c82d2024-12-04 20:19:40 +01003319 vartype = VAR_CLASS;
Ernie Raele6c9aa52023-10-06 19:55:52 +02003320 }
3321 }
3322 if (cl_tmp == NULL)
3323 break; // member is not in this or any super class.
3324 cl_found = cl_tmp;
3325 m_found = m;
3326 }
3327 if (p_m != NULL)
3328 *p_m = m_found;
3329 return cl_found;
3330}
3331
3332/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003333 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
3334 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
3335 *
3336 * Returns a pointer to the method structure if variable is found.
3337 * Otherwise returns NULL. The method variable index is set in "*idx".
3338 */
3339 ufunc_T *
3340method_lookup(
3341 class_T *cl,
3342 vartype_T v_type,
3343 char_u *name,
3344 size_t namelen,
3345 int *idx)
3346{
3347 if (v_type == VAR_CLASS)
3348 return class_method_lookup(cl, name, namelen, idx);
3349 else
3350 return object_method_lookup(cl, name, namelen, idx);
3351}
3352
3353/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00003354 * Return TRUE if current context "cctx_arg" is inside class "cl".
3355 * Return FALSE if not.
3356 */
3357 int
3358inside_class(cctx_T *cctx_arg, class_T *cl)
3359{
3360 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02003361 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003362 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00003363 return TRUE;
3364 return FALSE;
3365}
3366
3367/*
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01003368 * Return TRUE if object/class variable "m" is read-only.
3369 * Also give an error message.
3370 */
3371 int
3372oc_var_check_ro(class_T *cl, ocmember_T *m)
3373{
3374 if (m->ocm_flags & (OCMFLAG_FINAL | OCMFLAG_CONST))
3375 {
3376 semsg(_(e_cannot_change_readonly_variable_str_in_class_str),
3377 m->ocm_name, cl->class_name);
3378 return TRUE;
3379 }
3380 return FALSE;
3381}
3382
3383/*
3384 * Lock all the constant object variables. Called after creating and
3385 * initializing a new object.
3386 */
3387 void
3388obj_lock_const_vars(object_T *obj)
3389{
3390 for (int i = 0; i < obj->obj_class->class_obj_member_count; i++)
3391 {
3392 ocmember_T *ocm = &obj->obj_class->class_obj_members[i];
3393 if (ocm->ocm_flags & OCMFLAG_CONST)
3394 {
3395 typval_T *mtv = ((typval_T *)(obj + 1)) + i;
3396 item_lock(mtv, DICT_MAXNEST, TRUE, TRUE);
3397 }
3398 }
3399}
3400
3401/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003402 * Make a copy of an object.
3403 */
3404 void
3405copy_object(typval_T *from, typval_T *to)
3406{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003407 if (from->vval.v_object == NULL)
3408 to->vval.v_object = NULL;
3409 else
3410 {
3411 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003412 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003413 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003414}
3415
3416/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003417 * Make a copy of a class.
3418 */
3419 void
3420copy_class(typval_T *from, typval_T *to)
3421{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003422 if (from->vval.v_class == NULL)
3423 to->vval.v_class = NULL;
3424 else
3425 {
3426 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003427 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003428 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003429}
3430
3431/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003432 * Free the class "cl" and its contents.
3433 */
3434 static void
3435class_free(class_T *cl)
3436{
3437 // Freeing what the class contains may recursively come back here.
3438 // Clear "class_name" first, if it is NULL the class does not need to
3439 // be freed.
3440 VIM_CLEAR(cl->class_name);
3441
3442 class_unref(cl->class_extends);
3443
3444 for (int i = 0; i < cl->class_interface_count; ++i)
3445 {
3446 vim_free(((char_u **)cl->class_interfaces)[i]);
3447 if (cl->class_interfaces_cl[i] != NULL)
3448 class_unref(cl->class_interfaces_cl[i]);
3449 }
3450 vim_free(cl->class_interfaces);
3451 vim_free(cl->class_interfaces_cl);
3452
3453 itf2class_T *next;
3454 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
3455 {
3456 next = i2c->i2c_next;
3457 vim_free(i2c);
3458 }
3459
3460 for (int i = 0; i < cl->class_class_member_count; ++i)
3461 {
3462 ocmember_T *m = &cl->class_class_members[i];
3463 vim_free(m->ocm_name);
3464 vim_free(m->ocm_init);
3465 if (cl->class_members_tv != NULL)
3466 clear_tv(&cl->class_members_tv[i]);
3467 }
3468 vim_free(cl->class_class_members);
3469 vim_free(cl->class_members_tv);
3470
3471 for (int i = 0; i < cl->class_obj_member_count; ++i)
3472 {
3473 ocmember_T *m = &cl->class_obj_members[i];
3474 vim_free(m->ocm_name);
3475 vim_free(m->ocm_init);
3476 }
3477 vim_free(cl->class_obj_members);
3478
3479 for (int i = 0; i < cl->class_class_function_count; ++i)
3480 {
3481 ufunc_T *uf = cl->class_class_functions[i];
3482 func_clear_free(uf, FALSE);
3483 }
3484 vim_free(cl->class_class_functions);
3485
3486 for (int i = 0; i < cl->class_obj_method_count; ++i)
3487 {
3488 ufunc_T *uf = cl->class_obj_methods[i];
3489 func_clear_free(uf, FALSE);
3490 }
3491 vim_free(cl->class_obj_methods);
3492
3493 clear_type_list(&cl->class_type_list);
3494
3495 class_cleared(cl);
3496
3497 vim_free(cl);
3498}
3499
3500/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003501 * Unreference a class. Free it when the reference count goes down to zero.
3502 */
3503 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003504class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003505{
Bram Moolenaard505d172022-12-18 21:42:55 +00003506 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003507 class_free(cl);
3508}
3509
3510/*
3511 * Go through the list of all classes and free items without "copyID".
3512 */
3513 int
3514class_free_nonref(int copyID)
3515{
3516 int did_free = FALSE;
3517
3518 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003519 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003520 next_nonref_class = cl->class_next_used;
3521 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003522 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003523 // Free the class and items it contains.
3524 class_free(cl);
3525 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003526 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003527 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003528
3529 next_nonref_class = NULL;
3530 return did_free;
3531}
3532
3533 int
3534set_ref_in_classes(int copyID)
3535{
3536 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3537 set_ref_in_item_class(cl, copyID, NULL, NULL);
3538
3539 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003540}
3541
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003542static object_T *first_object = NULL;
3543
3544/*
3545 * Call this function when an object has been created. It will be added to the
3546 * list headed by "first_object".
3547 */
3548 void
3549object_created(object_T *obj)
3550{
3551 if (first_object != NULL)
3552 {
3553 obj->obj_next_used = first_object;
3554 first_object->obj_prev_used = obj;
3555 }
3556 first_object = obj;
3557}
3558
3559/*
3560 * Call this function when an object has been cleared and is about to be freed.
3561 * It is removed from the list headed by "first_object".
3562 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003563 static void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003564object_cleared(object_T *obj)
3565{
3566 if (obj->obj_next_used != NULL)
3567 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
3568 if (obj->obj_prev_used != NULL)
3569 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
3570 else if (first_object == obj)
3571 first_object = obj->obj_next_used;
3572}
3573
3574/*
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003575 * Free the contents of an object ignoring the reference count.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003576 */
3577 static void
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003578object_free_contents(object_T *obj)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003579{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003580 class_T *cl = obj->obj_class;
3581
3582 if (!cl)
3583 return;
3584
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003585 // Avoid a recursive call, it can happen if "obj" has a circular reference.
3586 obj->obj_refcount = INT_MAX;
3587
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003588 // the member values are just after the object structure
3589 typval_T *tv = (typval_T *)(obj + 1);
3590 for (int i = 0; i < cl->class_obj_member_count; ++i)
3591 clear_tv(tv + i);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003592}
3593
3594 static void
3595object_free_object(object_T *obj)
3596{
3597 class_T *cl = obj->obj_class;
3598
3599 if (!cl)
3600 return;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003601
3602 // Remove from the list headed by "first_object".
3603 object_cleared(obj);
3604
3605 vim_free(obj);
3606 class_unref(cl);
3607}
3608
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003609 static void
3610object_free(object_T *obj)
3611{
3612 if (in_free_unref_items)
3613 return;
3614
3615 object_free_contents(obj);
3616 object_free_object(obj);
3617}
3618
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003619/*
3620 * Unreference an object.
3621 */
3622 void
3623object_unref(object_T *obj)
3624{
3625 if (obj != NULL && --obj->obj_refcount <= 0)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003626 object_free(obj);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003627}
3628
3629/*
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003630 * Go through the list of all objects and free items without "copyID".
3631 */
3632 int
3633object_free_nonref(int copyID)
3634{
3635 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003636
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003637 for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003638 {
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003639 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3640 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003641 // Free the object contents. Object itself will be freed later.
3642 object_free_contents(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003643 did_free = TRUE;
3644 }
3645 }
3646
3647 return did_free;
3648}
3649
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003650 void
3651object_free_items(int copyID)
3652{
3653 object_T *obj_next;
3654
3655 for (object_T *obj = first_object; obj != NULL; obj = obj_next)
3656 {
3657 obj_next = obj->obj_next_used;
3658 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3659 object_free_object(obj);
3660 }
3661}
3662
LemonBoyafe04662023-08-23 21:08:11 +02003663/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003664 * Output message which takes a variable name and the class that defines it.
3665 * "cl" is that class where the name was found. Search "cl"'s hierarchy to
3666 * find the defining class.
3667 */
3668 void
3669emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl)
3670{
3671 ocmember_T *m;
3672 class_T *cl_def = class_defining_member(cl, name, len, &m);
3673 if (cl_def != NULL)
3674 semsg(_(msg), m->ocm_name, cl_def->class_name);
3675 else
3676 emsg(_(e_internal_error_please_report_a_bug));
3677}
3678
3679/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003680 * Echo a class or object method not found message.
3681 */
3682 void
3683method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3684{
3685 char_u *method_name = vim_strnsave(name, len);
3686 if ((v_type == VAR_OBJECT)
3687 && (class_method_idx(cl, name, len) >= 0))
3688 {
3689 // If this is a class method, then give a different error
3690 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003691 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003692 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003693 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003694 method_name, cl->class_name);
3695 }
3696 else if ((v_type == VAR_CLASS)
3697 && (object_method_idx(cl, name, len) >= 0))
3698 {
3699 // If this is an object method, then give a different error
3700 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003701 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003702 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003703 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003704 method_name, cl->class_name);
3705 }
3706 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003707 semsg(_(e_method_not_found_on_class_str_str), method_name,
zeertzjqd9be94c2024-07-14 10:20:20 +02003708 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003709 vim_free(method_name);
3710}
3711
3712/*
3713 * Echo a class or object member not found message.
3714 */
3715 void
3716member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3717{
3718 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
3719
3720 if (v_type == VAR_OBJECT)
3721 {
3722 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003723 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003724 varname, cl->class_name);
3725 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003726 semsg(_(e_variable_not_found_on_object_str_str), varname,
3727 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003728 }
3729 else
3730 {
3731 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003732 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003733 varname, cl->class_name);
3734 else
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01003735 {
3736 if (IS_ENUM(cl))
3737 semsg(_(e_enum_value_str_not_found_in_enum_str),
3738 varname, cl->class_name);
3739 else
3740 semsg(_(e_class_variable_str_not_found_in_class_str),
3741 varname, cl->class_name);
3742 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003743 }
3744 vim_free(varname);
3745}
3746
3747/*
Yegappan Lakshmanan4f32c832024-01-12 17:36:40 +01003748 * Compile all the class and object methods in "cl".
3749 */
3750 void
3751defcompile_class(class_T *cl)
3752{
3753 for (int loop = 1; loop <= 2; ++loop)
3754 {
3755 int func_count = loop == 1 ? cl->class_class_function_count
3756 : cl->class_obj_method_count;
3757 for (int i = 0; i < func_count; i++)
3758 {
3759 ufunc_T *ufunc = loop == 1 ? cl->class_class_functions[i]
3760 : cl->class_obj_methods[i];
Yegappan Lakshmanan1af0fbf2024-04-09 21:39:27 +02003761 // Don't compile abstract methods
3762 if (!IS_ABSTRACT_METHOD(ufunc))
3763 defcompile_function(ufunc, cl);
Yegappan Lakshmanan4f32c832024-01-12 17:36:40 +01003764 }
3765 }
3766}
3767
3768/*
3769 * Compile all the classes defined in the current script
3770 */
3771 void
3772defcompile_classes_in_script(void)
3773{
3774 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3775 {
3776 if (eval_variable(cl->class_name, 0, 0, NULL, NULL,
3777 EVAL_VAR_NOAUTOLOAD | EVAL_VAR_NO_FUNC) != FAIL)
3778 defcompile_class(cl);
3779 }
3780}
3781
3782/*
3783 * Returns TRUE if "name" is the name of a class. The typval for the class is
3784 * returned in "rettv".
3785 */
3786 int
3787is_class_name(char_u *name, typval_T *rettv)
3788{
3789 rettv->v_type = VAR_UNKNOWN;
3790
3791 if (eval_variable(name, 0, 0, rettv, NULL, EVAL_VAR_NOAUTOLOAD |
3792 EVAL_VAR_NO_FUNC) != FAIL)
3793 return rettv->v_type == VAR_CLASS;
3794 return FALSE;
3795}
3796
3797/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003798 * Calls the object builtin method "name" with arguments "argv". The value
3799 * returned by the builtin method is in "rettv". Returns OK or FAIL.
3800 */
3801 static int
3802object_call_builtin_method(
3803 object_T *obj,
3804 class_builtin_T builtin_method,
3805 int argc,
3806 typval_T *argv,
3807 typval_T *rettv)
3808{
3809 ufunc_T *uf;
3810 int midx;
3811
3812 if (obj == NULL)
3813 return FAIL;
3814
3815 uf = class_get_builtin_method(obj->obj_class, builtin_method, &midx);
3816 if (uf == NULL)
3817 return FAIL;
3818
3819 funccall_T *fc = create_funccal(uf, rettv);
3820 int r;
3821
3822 if (fc == NULL)
3823 return FAIL;
3824
3825 ++obj->obj_refcount;
3826
3827 r = call_def_function(uf, argc, argv, 0, NULL, obj, fc, rettv);
3828
3829 remove_funccal();
3830
3831 return r;
3832}
3833
3834/*
zeertzjqc029c132024-03-28 11:37:26 +01003835 * Calls the object "empty()" method and returns the method return value. In
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003836 * case of an error, returns TRUE.
3837 */
3838 int
3839object_empty(object_T *obj)
3840{
3841 typval_T rettv;
3842
3843 if (object_call_builtin_method(obj, CLASS_BUILTIN_EMPTY, 0, NULL, &rettv)
3844 == FAIL)
3845 return TRUE;
3846
3847 return tv_get_bool(&rettv);
3848}
3849
3850/*
3851 * Use the object "len()" method to get an object length. Returns 0 if the
3852 * method is not found or there is an error.
3853 */
3854 int
3855object_len(object_T *obj)
3856{
3857 typval_T rettv;
3858
3859 if (object_call_builtin_method(obj, CLASS_BUILTIN_LEN, 0, NULL, &rettv)
3860 == FAIL)
3861 return 0;
3862
3863 return tv_to_number(&rettv);
3864}
3865
3866/*
LemonBoy7b29cc92024-06-22 17:25:07 +02003867 * Return TRUE when two objects have exactly the same values.
3868 */
3869 int
3870object_equal(
3871 object_T *o1,
3872 object_T *o2,
Yinzuo Jiang7ccd1a22024-07-04 17:20:53 +02003873 int ic) // ignore case for strings
LemonBoy7b29cc92024-06-22 17:25:07 +02003874{
3875 class_T *cl1, *cl2;
3876
3877 if (o1 == o2)
3878 return TRUE;
Ernie Rael86257142024-06-23 09:54:45 +02003879 if (o1 == NULL || o2 == NULL)
3880 return FALSE;
LemonBoy7b29cc92024-06-22 17:25:07 +02003881
3882 cl1 = o1->obj_class;
3883 cl2 = o2->obj_class;
3884
3885 if (cl1 != cl2 || cl1 == NULL || cl2 == NULL)
3886 return FALSE;
3887
3888 for (int i = 0; i < cl1->class_obj_member_count; ++i)
Yinzuo Jiang7ccd1a22024-07-04 17:20:53 +02003889 if (!tv_equal((typval_T *)(o1 + 1) + i, (typval_T *)(o2 + 1) + i, ic))
LemonBoy7b29cc92024-06-22 17:25:07 +02003890 return FALSE;
3891
3892 return TRUE;
3893}
3894
3895/*
Ernie Rael05ff4e42024-07-04 16:50:11 +02003896 * Return a textual representation of object "obj".
3897 * "obj" must not be NULL.
3898 * May return NULL.
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003899 */
3900 char_u *
Yegappan Lakshmanan22029ed2024-05-20 13:57:11 +02003901object2string(
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003902 object_T *obj,
3903 char_u *numbuf,
3904 int copyID,
3905 int echo_style,
3906 int restore_copyID,
3907 int composite_val)
3908{
3909 typval_T rettv;
3910
3911 if (object_call_builtin_method(obj, CLASS_BUILTIN_STRING, 0, NULL, &rettv)
3912 == OK
3913 && rettv.vval.v_string != NULL)
3914 return rettv.vval.v_string;
Ernie Rael05ff4e42024-07-04 16:50:11 +02003915
3916 int ok = OK;
3917 class_T *cl = obj->obj_class;
3918 garray_T ga;
3919 ga_init2(&ga, 1, 50);
3920
3921 if (cl != NULL && IS_ENUM(cl))
3922 {
3923 ga_concat(&ga, (char_u *)"enum ");
3924 ga_concat(&ga, cl->class_name);
3925 char_u *enum_name = ((typval_T *)(obj + 1))->vval.v_string;
3926 ga_concat(&ga, (char_u *)".");
3927 ga_concat(&ga, enum_name);
3928 }
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003929 else
3930 {
Ernie Rael05ff4e42024-07-04 16:50:11 +02003931 ga_concat(&ga, (char_u *)"object of ");
3932 ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]"
3933 : cl->class_name);
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003934 }
Ernie Rael05ff4e42024-07-04 16:50:11 +02003935 if (cl != NULL)
3936 {
3937 ga_concat(&ga, (char_u *)" {");
3938 for (int i = 0; i < cl->class_obj_member_count; ++i)
3939 {
3940 if (i > 0)
3941 ga_concat(&ga, (char_u *)", ");
3942 ocmember_T *m = &cl->class_obj_members[i];
3943 ga_concat(&ga, m->ocm_name);
3944 ga_concat(&ga, (char_u *)": ");
3945 char_u *tf = NULL;
3946 char_u *s = echo_string_core((typval_T *)(obj + 1) + i,
3947 &tf, numbuf, copyID, echo_style,
3948 restore_copyID, composite_val);
3949 if (s != NULL)
3950 ga_concat(&ga, s);
3951 vim_free(tf);
3952 if (s == NULL || did_echo_string_emsg)
3953 {
3954 ok = FAIL;
3955 break;
3956 }
3957 line_breakcheck();
3958 }
3959 ga_concat(&ga, (char_u *)"}");
3960 }
3961 if (ok == FAIL)
3962 {
3963 vim_free(ga.ga_data);
3964 return NULL;
3965 }
3966 return (char_u *)ga.ga_data;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003967}
3968
3969/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02003970 * Return TRUE when the class "cl", its base class or one of the implemented
3971 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02003972 */
3973 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003974class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02003975{
3976 if (cl == other_cl)
3977 return TRUE;
3978
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003979 // Recursively check the base classes.
3980 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02003981 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003982 if (cl == other_cl)
3983 return TRUE;
3984 // Check the implemented interfaces and the super interfaces
3985 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003986 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003987 class_T *intf = cl->class_interfaces_cl[i];
3988 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003989 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003990 if (intf == other_cl)
3991 return TRUE;
3992 // check the super interfaces
3993 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003994 }
3995 }
LemonBoyafe04662023-08-23 21:08:11 +02003996 }
3997
3998 return FALSE;
3999}
4000
4001/*
Ernie Rael2025af12023-12-12 16:58:00 +01004002 * "instanceof(object, classinfo, ...)" function
LemonBoyafe04662023-08-23 21:08:11 +02004003 */
4004 void
4005f_instanceof(typval_T *argvars, typval_T *rettv)
4006{
4007 typval_T *object_tv = &argvars[0];
4008 typval_T *classinfo_tv = &argvars[1];
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02004009 class_T *c;
LemonBoyafe04662023-08-23 21:08:11 +02004010
4011 rettv->vval.v_number = VVAL_FALSE;
4012
4013 if (check_for_object_arg(argvars, 0) == FAIL
Ernie Rael2025af12023-12-12 16:58:00 +01004014 || check_for_class_or_typealias_args(argvars, 1) == FAIL)
LemonBoyafe04662023-08-23 21:08:11 +02004015 return;
4016
Ernie Rael3da696d2023-09-19 20:14:18 +02004017 if (object_tv->vval.v_object == NULL)
4018 return;
4019
Ernie Rael2025af12023-12-12 16:58:00 +01004020 for (; classinfo_tv->v_type != VAR_UNKNOWN; ++classinfo_tv)
LemonBoyafe04662023-08-23 21:08:11 +02004021 {
Ernie Rael2025af12023-12-12 16:58:00 +01004022 if (classinfo_tv->v_type == VAR_TYPEALIAS)
4023 c = classinfo_tv->vval.v_typealias->ta_type->tt_class;
4024 else
4025 c = classinfo_tv->vval.v_class;
4026
4027 if (class_instance_of(object_tv->vval.v_object->obj_class, c))
LemonBoyafe04662023-08-23 21:08:11 +02004028 {
Ernie Rael2025af12023-12-12 16:58:00 +01004029 rettv->vval.v_number = VVAL_TRUE;
4030 return;
LemonBoyafe04662023-08-23 21:08:11 +02004031 }
4032 }
LemonBoyafe04662023-08-23 21:08:11 +02004033}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00004034
4035#endif // FEAT_EVAL