blob: b64032309317f5391e6cb711b2aaf7673b514bf5 [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 Lakshmanand4e4ecb2023-08-27 18:35:45 +0200139 // No type specified for the member. Set it to "any" and the correct
140 // type will be set when the object is instantiated.
Bram Moolenaard505d172022-12-18 21:42:55 +0000141 if (type == NULL)
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200142 type = &t_any;
Bram Moolenaard505d172022-12-18 21:42:55 +0000143
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200144 *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
145 // Free the memory pointed by expr_start.
Bram Moolenaard505d172022-12-18 21:42:55 +0000146 clear_evalarg(&evalarg, NULL);
147 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200148 else if (!valid_declaration_type(type))
Bram Moolenaard505d172022-12-18 21:42:55 +0000149 return FAIL;
150
151 *type_ret = type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000152 return OK;
153}
154
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100155typedef struct oc_newmember_S oc_newmember_T;
156struct oc_newmember_S
157{
158 garray_T *gap;
159 char_u *varname;
160 char_u *varname_end;
161 int has_public;
162 int has_final;
163 int has_type;
164 type_T *type;
165 char_u *init_expr;
166};
167
Bram Moolenaard505d172022-12-18 21:42:55 +0000168/*
169 * Add a member to an object or a class.
170 * Returns OK when successful, "init_expr" will be consumed then.
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100171 * Returns OK on success and FAIL on memory allocation failure (caller might
172 * need to free "init_expr").
Bram Moolenaard505d172022-12-18 21:42:55 +0000173 */
174 static int
175add_member(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200176 garray_T *gap,
177 char_u *varname,
178 char_u *varname_end,
179 int has_public,
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100180 int has_final,
181 int has_const,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200182 int has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200183 type_T *type,
184 char_u *init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +0000185{
186 if (ga_grow(gap, 1) == FAIL)
187 return FAIL;
188 ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
189 m->ocm_name = vim_strnsave(varname, varname_end - varname);
=?UTF-8?q?Ola=20S=C3=B6der?=d8742472023-03-05 13:12:32 +0000190 m->ocm_access = has_public ? VIM_ACCESS_ALL
191 : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100192 if (has_final)
193 m->ocm_flags |= OCMFLAG_FINAL;
194 if (has_const)
195 m->ocm_flags |= OCMFLAG_CONST;
196 if (has_type)
197 m->ocm_flags |= OCMFLAG_HAS_TYPE;
Bram Moolenaard505d172022-12-18 21:42:55 +0000198 m->ocm_type = type;
199 if (init_expr != NULL)
200 m->ocm_init = init_expr;
201 ++gap->ga_len;
202 return OK;
203}
204
205/*
206 * Move the class or object members found while parsing a class into the class.
207 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000208 * "parent_members" points to the members in the parent class (if any)
209 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000210 * "members" will be set to the newly allocated array of members and
211 * "member_count" set to the number of members.
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100212 * Returns OK on success and FAIL on memory allocation failure.
Bram Moolenaard505d172022-12-18 21:42:55 +0000213 */
214 static int
215add_members_to_class(
216 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000217 ocmember_T *parent_members,
218 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000219 ocmember_T **members,
220 int *member_count)
221{
Bram Moolenaar83677162023-01-08 19:54:10 +0000222 *member_count = parent_count + gap->ga_len;
223 *members = *member_count == 0 ? NULL
224 : ALLOC_MULT(ocmember_T, *member_count);
225 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000226 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000227 for (int i = 0; i < parent_count; ++i)
228 {
229 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000230 ocmember_T *m = *members + i;
231 *m = parent_members[i];
232 m->ocm_name = vim_strsave(m->ocm_name);
233 if (m->ocm_init != NULL)
234 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000235 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000236 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000237 // new members are moved
238 mch_memmove(*members + parent_count,
239 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000240 VIM_CLEAR(gap->ga_data);
241 return OK;
242}
243
244/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000245 * Convert a member index "idx" of interface "itf" to the member index of class
246 * "cl" implementing that interface.
247 */
248 int
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200249object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000250{
Ernie Rael18143d32023-09-04 22:30:41 +0200251 if (idx >= (is_method ? itf->class_obj_method_count
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200252 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000253 {
254 siemsg("index %d out of range for interface %s", idx, itf->class_name);
255 return 0;
256 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200257
258 // If "cl" is the interface or the class that is extended, then the method
259 // index can be used directly and there is no need to search for the method
260 // index in one of the child classes.
261 if (cl == itf)
262 return idx;
263
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200264 itf2class_T *i2c = NULL;
265 int searching = TRUE;
266 int method_offset = 0;
267
Ernie Raelcf138d42023-09-06 20:45:03 +0200268 for (class_T *super = cl; super != NULL && searching;
269 super = super->class_extends)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200270 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200271 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200272 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200273 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
274 {
275 searching = FALSE;
276 break;
277 }
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200278 }
279 if (searching && is_method)
280 // The parent class methods are stored after the current class
281 // methods.
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200282 method_offset += super->class_obj_method_count_child;
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200283 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000284 if (i2c == NULL)
285 {
286 siemsg("class %s not found on interface %s",
287 cl->class_name, itf->class_name);
288 return 0;
289 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +0200290
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200291 // A table follows the i2c for the class
292 int *table = (int *)(i2c + 1);
293 // "method_offset" is 0, if method is in the current class. If method
294 // is in a parent class, then it is non-zero.
295 return table[idx] + method_offset;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000296}
297
298/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200299 * Check whether a class named "extends_name" is present. If the class is
300 * valid, then "extends_clp" is set with the class pointer.
301 * Returns TRUE if the class name "extends_names" is a valid class.
302 */
303 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200304validate_extends_class(
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100305 class_T *cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200306 char_u *extends_name,
307 class_T **extends_clp,
308 int is_class)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200309{
310 typval_T tv;
311 int success = FALSE;
312
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100313 if (STRCMP(cl->class_name, extends_name) == 0)
314 {
315 semsg(_(e_cannot_extend_str), extends_name);
316 return success;
317 }
318
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200319 tv.v_type = VAR_UNKNOWN;
320 if (eval_variable_import(extends_name, &tv) == FAIL)
321 {
322 semsg(_(e_class_name_not_found_str), extends_name);
323 return success;
324 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200325
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200326 if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100327 || (is_class && IS_INTERFACE(tv.vval.v_class))
328 || (!is_class && !IS_INTERFACE(tv.vval.v_class))
329 || (is_class && IS_ENUM(tv.vval.v_class)))
330 {
331 // a class cannot extend an interface
332 // an interface cannot extend a class
333 // a class cannot extend an enum.
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200334 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100335 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200336 else
337 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200338 class_T *extends_cl = tv.vval.v_class;
339 ++extends_cl->class_refcount;
340 *extends_clp = extends_cl;
341 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200342 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200343 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200344
345 return success;
346}
347
348/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200349 * Check method names in the parent class lineage to make sure the access is
350 * the same for overridden methods.
351 */
352 static int
353validate_extends_methods(
354 garray_T *objmethods_gap,
355 class_T *extends_cl)
356{
357 class_T *super = extends_cl;
358 int method_count = objmethods_gap->ga_len;
359 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
360
361 while (super != NULL)
362 {
363 int extends_method_count = super->class_obj_method_count_child;
364 if (extends_method_count == 0)
365 {
366 super = super->class_extends;
367 continue;
368 }
369
370 ufunc_T **extends_methods = super->class_obj_methods;
371
372 for (int i = 0; i < extends_method_count; i++)
373 {
374 char_u *pstr = extends_methods[i]->uf_name;
375 int extends_private = (*pstr == '_');
376 if (extends_private)
377 pstr++;
378
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200379 // When comparing the method names, ignore the access type (public
380 // and private methods are considered the same).
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200381 for (int j = 0; j < method_count; j++)
382 {
383 char_u *qstr = cl_fp[j]->uf_name;
384 int priv_method = (*qstr == '_');
385 if (priv_method)
386 qstr++;
387 if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
388 {
389 // Method access is different between the super class and
390 // the subclass
391 semsg(_(e_method_str_of_class_str_has_different_access),
392 cl_fp[j]->uf_name, super->class_name);
393 return FALSE;
394 }
395 }
396 }
397 super = super->class_extends;
398 }
399
400 return TRUE;
401}
402
403/*
404 * Check whether a object member variable in "objmembers_gap" is a duplicate of
405 * a member in any of the extended parent class lineage. Returns TRUE if there
406 * are no duplicates.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200407 */
408 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200409extends_check_dup_members(
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200410 garray_T *objmembers_gap,
411 class_T *extends_cl)
412{
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200413 int member_count = objmembers_gap->ga_len;
414 if (member_count == 0)
415 return TRUE;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200416
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200417 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
418
419 // Validate each member variable
420 for (int c_i = 0; c_i < member_count; c_i++)
421 {
422 class_T *p_cl = extends_cl;
423 ocmember_T *c_m = members + c_i;
424 char_u *pstr = (*c_m->ocm_name == '_')
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200425 ? c_m->ocm_name + 1 : c_m->ocm_name;
426
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200427 // Check in all the parent classes in the lineage
428 while (p_cl != NULL)
429 {
430 int p_member_count = p_cl->class_obj_member_count;
431 if (p_member_count == 0)
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200432 {
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200433 p_cl = p_cl->class_extends;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200434 continue;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200435 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200436 ocmember_T *p_members = p_cl->class_obj_members;
437
438 // Compare against all the members in the parent class
439 for (int p_i = 0; p_i < p_member_count; p_i++)
440 {
441 ocmember_T *p_m = p_members + p_i;
442 char_u *qstr = (*p_m->ocm_name == '_')
443 ? p_m->ocm_name + 1 : p_m->ocm_name;
444 if (STRCMP(pstr, qstr) == 0)
445 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200446 semsg(_(e_duplicate_variable_str), c_m->ocm_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200447 return FALSE;
448 }
449 }
450
451 p_cl = p_cl->class_extends;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200452 }
453 }
454
455 return TRUE;
456}
457
458/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200459 * Compare the variable type of interface variables in "objmembers_gap" against
460 * the variable in any of the extended super interface lineage. Used to
461 * compare the variable types when extending interfaces. Returns TRUE if the
462 * variable types are the same.
463 */
464 static int
465extends_check_intf_var_type(
466 garray_T *objmembers_gap,
467 class_T *extends_cl)
468{
469 int member_count = objmembers_gap->ga_len;
470 if (member_count == 0)
471 return TRUE;
472
473 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
474
475 // Validate each member variable
476 for (int c_i = 0; c_i < member_count; c_i++)
477 {
478 class_T *p_cl = extends_cl;
479 ocmember_T *c_m = members + c_i;
480 int var_found = FALSE;
481
482 // Check in all the parent classes in the lineage
483 while (p_cl != NULL && !var_found)
484 {
485 int p_member_count = p_cl->class_obj_member_count;
486 if (p_member_count == 0)
487 {
488 p_cl = p_cl->class_extends;
489 continue;
490 }
491 ocmember_T *p_members = p_cl->class_obj_members;
492
493 // Compare against all the members in the parent class
494 for (int p_i = 0; p_i < p_member_count; p_i++)
495 {
496 where_T where = WHERE_INIT;
497 ocmember_T *p_m = p_members + p_i;
498
499 if (STRCMP(p_m->ocm_name, c_m->ocm_name) != 0)
500 continue;
501
502 // Ensure the type is matching.
503 where.wt_func_name = (char *)c_m->ocm_name;
504 where.wt_kind = WT_MEMBER;
505
506 if (check_type(p_m->ocm_type, c_m->ocm_type, TRUE,
507 where) == FAIL)
508 return FALSE;
509
510 var_found = TRUE;
511 }
512
513 p_cl = p_cl->class_extends;
514 }
515 }
516
517 return TRUE;
518}
519
520/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200521 * When extending an abstract class, check whether all the abstract methods in
522 * the parent class are implemented. Returns TRUE if all the methods are
523 * implemented.
524 */
525 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200526validate_abstract_class_methods(
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200527 garray_T *classmethods_gap,
528 garray_T *objmethods_gap,
529 class_T *extends_cl)
530{
531 for (int loop = 1; loop <= 2; ++loop)
532 {
533 // loop == 1: check class methods
534 // loop == 2: check object methods
535 int extends_method_count = loop == 1
536 ? extends_cl->class_class_function_count
537 : extends_cl->class_obj_method_count;
538 if (extends_method_count == 0)
539 continue;
540
541 ufunc_T **extends_methods = loop == 1
542 ? extends_cl->class_class_functions
543 : extends_cl->class_obj_methods;
544
545 int method_count = loop == 1 ? classmethods_gap->ga_len
546 : objmethods_gap->ga_len;
547 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
548 ? classmethods_gap->ga_data
549 : objmethods_gap->ga_data);
550
551 for (int i = 0; i < extends_method_count; i++)
552 {
553 ufunc_T *uf = extends_methods[i];
Yegappan Lakshmanan1db15142023-09-19 20:34:05 +0200554 if (!IS_ABSTRACT_METHOD(uf))
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200555 continue;
556
557 int method_found = FALSE;
558
559 for (int j = 0; j < method_count; j++)
560 {
561 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
562 {
563 method_found = TRUE;
564 break;
565 }
566 }
567
568 if (!method_found)
569 {
570 semsg(_(e_abstract_method_str_not_found), uf->uf_name);
571 return FALSE;
572 }
573 }
574 }
575
576 return TRUE;
577}
578
579/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200580 * Returns TRUE if the interface variable "if_var" is present in the list of
581 * variables in "cl_mt" or in the parent lineage of one of the extended classes
582 * in "extends_cl". For a class variable, 'is_class_var' is TRUE.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200583 */
584 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200585intf_variable_present(
586 char_u *intf_class_name,
587 ocmember_T *if_var,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200588 ocmember_T *cl_mt,
589 int cl_member_count,
590 class_T *extends_cl)
591{
592 int variable_present = FALSE;
593
594 for (int cl_i = 0; cl_i < cl_member_count; ++cl_i)
595 {
596 ocmember_T *m = &cl_mt[cl_i];
597 where_T where = WHERE_INIT;
598
599 if (STRCMP(if_var->ocm_name, m->ocm_name) != 0)
600 continue;
601
602 // Ensure the access type is same
603 if (if_var->ocm_access != m->ocm_access)
604 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200605 semsg(_(e_variable_str_of_interface_str_has_different_access),
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200606 if_var->ocm_name, intf_class_name);
607 return FALSE;
608 }
609
610 // Ensure the type is matching.
611 if (m->ocm_type == &t_any)
612 {
613 // variable type is not specified. Use the variable type in the
614 // interface.
615 m->ocm_type = if_var->ocm_type;
616 }
617 else
618 {
619 where.wt_func_name = (char *)m->ocm_name;
620 where.wt_kind = WT_MEMBER;
621 if (check_type(if_var->ocm_type, m->ocm_type, TRUE,
622 where) == FAIL)
623 return FALSE;
624 }
625
626 variable_present = TRUE;
627 break;
628 }
629
630 if (!variable_present && extends_cl != NULL)
631 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200632 int ext_cl_count = extends_cl->class_obj_member_count;
633 ocmember_T *ext_cl_mt = extends_cl->class_obj_members;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200634 return intf_variable_present(intf_class_name, if_var,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200635 ext_cl_mt, ext_cl_count,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200636 extends_cl->class_extends);
637 }
638
639 return variable_present;
640}
641
642/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200643 * Check the variables of the interface class "ifcl" match object variables
644 * ("objmembers_gap") of a class.
645 * Returns TRUE if the object variables names are valid.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200646 */
647 static int
648validate_interface_variables(
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200649 char_u *intf_class_name,
650 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200651 garray_T *objmembers_gap,
652 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200653{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200654 int if_count = ifcl->class_obj_member_count;
655 if (if_count == 0)
656 return TRUE;
657
658 ocmember_T *if_ms = ifcl->class_obj_members;
659 ocmember_T *cl_ms = (ocmember_T *)(objmembers_gap->ga_data);
660 int cl_count = objmembers_gap->ga_len;
661 for (int if_i = 0; if_i < if_count; ++if_i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200662 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200663 if (!intf_variable_present(intf_class_name, &if_ms[if_i], cl_ms,
664 cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200665 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200666 semsg(_(e_variable_str_of_interface_str_not_implemented),
667 if_ms[if_i].ocm_name, intf_class_name);
668 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200669 }
670 }
671
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200672 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200673}
674
675/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200676 * Returns TRUE if the method signature of "if_method" and "cl_method" matches.
677 */
678 static int
679intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method)
680{
681 where_T where = WHERE_INIT;
682
683 // Ensure the type is matching.
684 where.wt_func_name = (char *)if_method->uf_name;
685 where.wt_kind = WT_METHOD;
686 if (check_type(if_method->uf_func_type, cl_method->uf_func_type, TRUE,
687 where) == FAIL)
688 return FALSE;
689
690 return TRUE;
691}
692
693/*
694 * Returns TRUE if the interface method "if_ufunc" is present in the list of
695 * methods in "cl_fp" or in the parent lineage of one of the extended classes
696 * in "extends_cl". For a class method, 'is_class_method' is TRUE.
697 */
698 static int
699intf_method_present(
700 ufunc_T *if_ufunc,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200701 ufunc_T **cl_fp,
702 int cl_count,
703 class_T *extends_cl)
704{
705 int method_present = FALSE;
706
707 for (int cl_i = 0; cl_i < cl_count; ++cl_i)
708 {
709 char_u *cl_name = cl_fp[cl_i]->uf_name;
710 if (STRCMP(if_ufunc->uf_name, cl_name) == 0)
711 {
712 // Ensure the type is matching.
713 if (!intf_method_type_matches(if_ufunc, cl_fp[cl_i]))
714 return FALSE;
715 method_present = TRUE;
716 break;
717 }
718 }
719
720 if (!method_present && extends_cl != NULL)
721 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200722 ufunc_T **ext_cl_fp = (ufunc_T **)(extends_cl->class_obj_methods);
723 int ext_cl_count = extends_cl->class_obj_method_count;
724 return intf_method_present(if_ufunc, ext_cl_fp, ext_cl_count,
725 extends_cl->class_extends);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200726 }
727
728 return method_present;
729}
730
731/*
732 * Validate that a new class implements all the class/instance methods in the
733 * interface "ifcl". The new class methods are in "classfunctions_gap" and the
734 * new object methods are in "objmemthods_gap". Also validates the method
735 * types.
736 * Returns TRUE if all the interface class/object methods are implemented in
737 * the new class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200738 */
739 static int
740validate_interface_methods(
741 char_u *intf_class_name,
742 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200743 garray_T *objmethods_gap,
744 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200745{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200746 int if_count = ifcl->class_obj_method_count;
747 if (if_count == 0)
748 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200749
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200750 ufunc_T **if_fp = ifcl->class_obj_methods;
751 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
752 int cl_count = objmethods_gap->ga_len;
753 for (int if_i = 0; if_i < if_count; ++if_i)
754 {
755 char_u *if_name = if_fp[if_i]->uf_name;
756
757 if (!intf_method_present(if_fp[if_i], cl_fp, cl_count, extends_cl))
758 {
759 semsg(_(e_method_str_of_interface_str_not_implemented),
760 if_name, intf_class_name);
761 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200762 }
763 }
764
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200765 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200766}
767
768/*
769 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200770 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200771 * object methods and object members in the new class are in
772 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
773 * "objmembers_gap" respectively.
774 */
775 static int
776validate_implements_classes(
777 garray_T *impl_gap,
778 class_T **intf_classes,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200779 garray_T *objmethods_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200780 garray_T *objmembers_gap,
781 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200782{
783 int success = TRUE;
784
785 for (int i = 0; i < impl_gap->ga_len && success; ++i)
786 {
787 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
788 typval_T tv;
789 tv.v_type = VAR_UNKNOWN;
790 if (eval_variable_import(impl, &tv) == FAIL)
791 {
792 semsg(_(e_interface_name_not_found_str), impl);
793 success = FALSE;
794 break;
795 }
796
797 if (tv.v_type != VAR_CLASS
798 || tv.vval.v_class == NULL
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +0100799 || !IS_INTERFACE(tv.vval.v_class))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200800 {
801 semsg(_(e_not_valid_interface_str), impl);
802 success = FALSE;
803 clear_tv(&tv);
804 break;
805 }
806
807 class_T *ifcl = tv.vval.v_class;
808 intf_classes[i] = ifcl;
809 ++ifcl->class_refcount;
810
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200811 // check the variables of the interface match the members of the class
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200812 success = validate_interface_variables(impl, ifcl, objmembers_gap,
813 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200814
815 // check the functions/methods of the interface match the
816 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200817 if (success)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200818 success = validate_interface_methods(impl, ifcl, objmethods_gap,
819 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200820 clear_tv(&tv);
821 }
822
823 return success;
824}
825
826/*
827 * Check no function argument name is used as a class member.
828 * (Object members are always accessed with "this." prefix, so no need
829 * to check them.)
830 */
831 static int
832check_func_arg_names(
833 garray_T *classfunctions_gap,
834 garray_T *objmethods_gap,
835 garray_T *classmembers_gap)
836{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200837 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200838 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200839 {
840 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
841
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200842 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200843 {
844 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
845
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200846 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200847 {
848 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
849 garray_T *mgap = classmembers_gap;
850
851 // Check all the class member names
852 for (int mi = 0; mi < mgap->ga_len; ++mi)
853 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200854 char_u *mname =
855 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200856 if (STRCMP(aname, mname) == 0)
857 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200858 if (uf->uf_script_ctx.sc_sid > 0)
859 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
860
861 semsg(_(e_argument_already_declared_in_class_str),
862 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200863
864 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200865 }
866 }
867 }
868 }
869 }
870
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200871 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200872}
873
874/*
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200875 * Returns TRUE if 'varname' is a reserved keyword name
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200876 */
877 static int
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200878is_reserved_varname(char_u *varname, char_u *varname_end)
879{
880 int reserved = FALSE;
881 char_u save_varname_end = *varname_end;
882 *varname_end = NUL;
883
884 reserved = check_reserved_name(varname, FALSE) == FAIL;
885
886 *varname_end = save_varname_end;
887
888 return reserved;
889}
890
891/*
892 * Returns TRUE if the variable "varname" is already defined either as a class
893 * variable or as an object variable.
894 */
895 static int
896is_duplicate_variable(
897 garray_T *class_members,
898 garray_T *obj_members,
899 char_u *varname,
900 char_u *varname_end)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200901{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200902 char_u *name = vim_strnsave(varname, varname_end - varname);
903 char_u *pstr = (*name == '_') ? name + 1 : name;
904 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200905
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200906 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200907 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200908 // loop == 1: class variables, loop == 2: object variables
909 garray_T *vgap = (loop == 1) ? class_members : obj_members;
910 for (int i = 0; i < vgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200911 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200912 ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i;
913 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1
914 : m->ocm_name;
915 if (STRCMP(pstr, qstr) == 0)
916 {
917 semsg(_(e_duplicate_variable_str), name);
918 dup = TRUE;
919 break;
920 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200921 }
922 }
923
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200924 vim_free(name);
925 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200926}
927
928/*
929 * Returns TRUE if the method "name" is already defined.
930 */
931 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200932is_duplicate_method(
933 garray_T *classmethods_gap,
934 garray_T *objmethods_gap,
935 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200936{
937 char_u *pstr = (*name == '_') ? name + 1 : name;
938
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200939 // loop 1: class methods, loop 2: object methods
940 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200941 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200942 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
943 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200944 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200945 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
946 char_u *qstr = *n == '_' ? n + 1 : n;
947 if (STRCMP(pstr, qstr) == 0)
948 {
949 semsg(_(e_duplicate_function_str), name);
950 return TRUE;
951 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200952 }
953 }
954
955 return FALSE;
956}
957
958/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200959 * Returns TRUE if the constructor is valid.
960 */
961 static int
962is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
963{
964 // Constructors are not allowed in abstract classes.
965 if (is_abstract)
966 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200967 emsg(_(e_cannot_define_new_method_in_abstract_class));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200968 return FALSE;
969 }
970 // A constructor is always static, no need to define it so.
971 if (has_static)
972 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200973 emsg(_(e_cannot_define_new_method_as_static));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200974 return FALSE;
975 }
976 // A return type should not be specified for the new()
977 // constructor method.
978 if (uf->uf_ret_type->tt_type != VAR_VOID)
979 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200980 emsg(_(e_cannot_use_a_return_type_with_new_method));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200981 return FALSE;
982 }
983 return TRUE;
984}
985
986/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +0100987 * Returns TRUE if 'uf' is a supported builtin method and has the correct
988 * method signature.
989 */
990 static int
991object_check_builtin_method_sig(ufunc_T *uf)
992{
993 char_u *name = uf->uf_name;
994 int valid = FALSE;
995 type_T method_sig;
996 type_T method_rt;
997 where_T where = WHERE_INIT;
998
999 // validate the method signature
1000 CLEAR_FIELD(method_sig);
1001 CLEAR_FIELD(method_rt);
1002 method_sig.tt_type = VAR_FUNC;
1003
1004 if (STRCMP(name, "len") == 0)
1005 {
1006 // def __len(): number
1007 method_rt.tt_type = VAR_NUMBER;
1008 method_sig.tt_member = &method_rt;
1009 valid = TRUE;
1010 }
1011 else if (STRCMP(name, "empty") == 0)
1012 {
1013 // def __empty(): bool
1014 method_rt.tt_type = VAR_BOOL;
1015 method_sig.tt_member = &method_rt;
1016 valid = TRUE;
1017 }
1018 else if (STRCMP(name, "string") == 0)
1019 {
1020 // def __string(): string
1021 method_rt.tt_type = VAR_STRING;
1022 method_sig.tt_member = &method_rt;
1023 valid = TRUE;
1024 }
1025 else
1026 semsg(_(e_builtin_object_method_str_not_supported), uf->uf_name);
1027
1028 where.wt_func_name = (char *)uf->uf_name;
1029 where.wt_kind = WT_METHOD;
1030 if (valid && !check_type(&method_sig, uf->uf_func_type, TRUE, where))
1031 valid = FALSE;
1032
1033 return valid;
1034}
1035
1036/*
1037 * Returns TRUE if "funcname" is a supported builtin object method name
1038 */
1039 int
1040is_valid_builtin_obj_methodname(char_u *funcname)
1041{
1042 switch (funcname[0])
1043 {
1044 case 'e':
1045 return STRNCMP(funcname, "empty", 5) == 0;
1046
1047 case 'l':
1048 return STRNCMP(funcname, "len", 3) == 0;
1049
1050 case 'n':
1051 return STRNCMP(funcname, "new", 3) == 0;
1052
1053 case 's':
1054 return STRNCMP(funcname, "string", 6) == 0;
1055 }
1056
1057 return FALSE;
1058}
1059
1060
1061/*
1062 * Returns the builtin method "name" in object "obj". Returns NULL if the
1063 * method is not found.
1064 */
1065 ufunc_T *
1066class_get_builtin_method(
1067 class_T *cl,
1068 class_builtin_T builtin_method,
1069 int *method_idx)
1070{
1071 *method_idx = -1;
1072
1073 if (cl == NULL)
1074 return NULL;
1075
1076 *method_idx = cl->class_builtin_methods[builtin_method];
1077 return *method_idx != -1 ? cl->class_obj_methods[*method_idx] : NULL;
1078}
1079
1080/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001081 * Update the interface class lookup table for the member index on the
1082 * interface to the member index in the class implementing the interface.
1083 * And a lookup table for the object method index on the interface
1084 * to the object method index in the class implementing the interface.
1085 * This is also used for updating the lookup table for the extended class
1086 * hierarchy.
1087 */
1088 static int
1089update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001090 class_T *ifcl,
1091 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001092 garray_T *objmethods,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001093 int pobj_method_offset)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001094{
1095 if (ifcl == NULL)
1096 return OK;
1097
1098 // Table for members.
1099 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001100 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001101 if (if2cl == NULL)
1102 return FAIL;
1103 if2cl->i2c_next = ifcl->class_itf2class;
1104 ifcl->class_itf2class = if2cl;
1105 if2cl->i2c_class = cl;
1106 if2cl->i2c_is_method = FALSE;
1107
1108 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
1109 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
1110 {
1111 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001112 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001113 {
1114 int *table = (int *)(if2cl + 1);
1115 table[if_i] = cl_i;
1116 break;
1117 }
1118 }
1119
1120 // Table for methods.
1121 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001122 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001123 if (if2cl == NULL)
1124 return FAIL;
1125 if2cl->i2c_next = ifcl->class_itf2class;
1126 ifcl->class_itf2class = if2cl;
1127 if2cl->i2c_class = cl;
1128 if2cl->i2c_is_method = TRUE;
1129
1130 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
1131 {
1132 int done = FALSE;
1133 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
1134 {
1135 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001136 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001137 {
1138 int *table = (int *)(if2cl + 1);
1139 table[if_i] = cl_i;
1140 done = TRUE;
1141 break;
1142 }
1143 }
1144
1145 // extended class object method is not overridden by the child class.
1146 // Keep the method declared in one of the parent classes in the
1147 // lineage.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001148 if (!done)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001149 {
1150 // If "ifcl" is not the immediate parent of "cl", then search in
1151 // the intermediate parent classes.
1152 if (cl->class_extends != ifcl)
1153 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001154 class_T *parent = cl->class_extends;
1155 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001156
1157 while (!done && parent != NULL && parent != ifcl)
1158 {
1159
1160 for (int cl_i = 0;
1161 cl_i < parent->class_obj_method_count_child; ++cl_i)
1162 {
1163 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
1164 parent->class_obj_methods[cl_i]->uf_name)
1165 == 0)
1166 {
1167 int *table = (int *)(if2cl + 1);
1168 table[if_i] = method_offset + cl_i;
1169 done = TRUE;
1170 break;
1171 }
1172 }
1173 method_offset += parent->class_obj_method_count_child;
1174 parent = parent->class_extends;
1175 }
1176 }
1177
1178 if (!done)
1179 {
1180 int *table = (int *)(if2cl + 1);
1181 table[if_i] = pobj_method_offset + if_i;
1182 }
1183 }
1184 }
1185
1186 return OK;
1187}
1188
1189/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001190 * Update the member and object method lookup tables for a new class in the
1191 * interface class.
1192 * For each interface add a lookup table for the member index on the interface
1193 * to the member index in the new class. And a lookup table for the object
1194 * method index on the interface to the object method index in the new class.
1195 */
1196 static int
1197add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
1198{
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001199 // update the lookup table for all the implemented interfaces
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001200 for (int i = 0; i < cl->class_interface_count; ++i)
1201 {
1202 class_T *ifcl = cl->class_interfaces_cl[i];
1203
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001204 // update the lookup table for this interface and all its super
1205 // interfaces.
1206 while (ifcl != NULL)
1207 {
1208 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
1209 0) == FAIL)
1210 return FAIL;
1211 ifcl = ifcl->class_extends;
1212 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001213 }
1214
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001215 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001216 if (extends_cl != NULL)
1217 {
1218 class_T *pclass = extends_cl;
1219 int pobj_method_offset = objmethods_gap->ga_len;
1220
1221 // Update the entire lineage of extended classes.
1222 while (pclass != NULL)
1223 {
1224 if (update_member_method_lookup_table(pclass, cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001225 objmethods_gap, pobj_method_offset) == FAIL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001226 return FAIL;
1227
1228 pobj_method_offset += pclass->class_obj_method_count_child;
1229 pclass = pclass->class_extends;
1230 }
1231 }
1232
1233 return OK;
1234}
1235
1236/*
1237 * Add class members to a new class. Allocate a typval for each class member
1238 * and initialize it.
1239 */
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001240 static int
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001241add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001242{
1243 // Allocate a typval for each class member and initialize it.
1244 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
1245 cl->class_class_member_count);
1246 if (cl->class_members_tv == NULL)
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001247 return FAIL;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001248
1249 for (int i = 0; i < cl->class_class_member_count; ++i)
1250 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001251 ocmember_T *m = &cl->class_class_members[i];
1252 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001253 if (m->ocm_init != NULL)
1254 {
1255 typval_T *etv = eval_expr(m->ocm_init, eap);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001256 if (etv == NULL)
1257 return FAIL;
1258
1259 if (m->ocm_type->tt_type == VAR_ANY
1260 && !(m->ocm_flags & OCMFLAG_HAS_TYPE)
1261 && etv->v_type != VAR_SPECIAL)
1262 // If the member variable type is not yet set, then use
1263 // the initialization expression type.
1264 m->ocm_type = typval2type(etv, get_copyID(),
1265 type_list_gap,
1266 TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
1267 *tv = *etv;
1268 vim_free(etv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001269 }
1270 else
1271 {
1272 // TODO: proper default value
1273 tv->v_type = m->ocm_type->tt_type;
1274 tv->vval.v_string = NULL;
1275 }
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001276 if (m->ocm_flags & OCMFLAG_CONST)
1277 item_lock(tv, DICT_MAXNEST, TRUE, TRUE);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001278 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001279
1280 return OK;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001281}
1282
1283/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001284 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001285 */
1286 static void
1287add_default_constructor(
1288 class_T *cl,
1289 garray_T *classfunctions_gap,
1290 garray_T *type_list_gap)
1291{
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001292 garray_T fga;
1293 int is_enum = IS_ENUM(cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001294
1295 ga_init2(&fga, 1, 1000);
1296 ga_concat(&fga, (char_u *)"new(");
1297 for (int i = 0; i < cl->class_obj_member_count; ++i)
1298 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001299 if (i < 2 && is_enum)
1300 // The first two object variables in an enum are the enum value
1301 // name and ordinal. Don't initialize these object variables in
1302 // the default constructor as they are already initialized right
1303 // after creating the object.
1304 continue;
1305
1306 if (i > (is_enum ? 2 : 0))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001307 ga_concat(&fga, (char_u *)", ");
1308 ga_concat(&fga, (char_u *)"this.");
1309 ocmember_T *m = cl->class_obj_members + i;
1310 ga_concat(&fga, (char_u *)m->ocm_name);
1311 ga_concat(&fga, (char_u *)" = v:none");
1312 }
1313 ga_concat(&fga, (char_u *)")\nenddef\n");
1314 ga_append(&fga, NUL);
1315
1316 exarg_T fea;
1317 CLEAR_FIELD(fea);
1318 fea.cmdidx = CMD_def;
1319 fea.cmd = fea.arg = fga.ga_data;
1320
1321 garray_T lines_to_free;
1322 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1323
h-eastb895b0f2023-09-24 15:46:31 +02001324 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
1325 cl->class_obj_members, cl->class_obj_member_count);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001326
1327 ga_clear_strings(&lines_to_free);
1328 vim_free(fga.ga_data);
1329
1330 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1331 {
1332 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001333 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001334 ++classfunctions_gap->ga_len;
1335
1336 nf->uf_flags |= FC_NEW;
1337 nf->uf_ret_type = get_type_ptr(type_list_gap);
1338 if (nf->uf_ret_type != NULL)
1339 {
1340 nf->uf_ret_type->tt_type = VAR_OBJECT;
1341 nf->uf_ret_type->tt_class = cl;
1342 nf->uf_ret_type->tt_argcount = 0;
1343 nf->uf_ret_type->tt_args = NULL;
1344 }
1345 }
1346}
1347
1348/*
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001349 * Add the class methods and object methods to the new class "cl".
1350 * When extending a class "extends_cl", add the instance methods from the
1351 * parent class also.
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001352 * Returns OK on success and FAIL on memory allocation failure.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001353 */
1354 static int
1355add_classfuncs_objmethods(
1356 class_T *cl,
1357 class_T *extends_cl,
1358 garray_T *classfunctions_gap,
1359 garray_T *objmethods_gap)
1360{
1361 // loop 1: class functions, loop 2: object methods
1362 for (int loop = 1; loop <= 2; ++loop)
1363 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001364 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1365 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001366 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001367 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001368 : &cl->class_obj_methods;
1369
1370 int parent_count = 0;
1371 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001372 // Include object methods from the parent.
1373 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001374 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001375 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001376 : extends_cl->class_obj_method_count;
1377
1378 *fcount = parent_count + gap->ga_len;
1379 if (*fcount == 0)
1380 {
1381 *fup = NULL;
1382 continue;
1383 }
1384 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1385 if (*fup == NULL)
1386 return FAIL;
1387
1388 if (gap->ga_len != 0)
1389 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001390 VIM_CLEAR(gap->ga_data);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001391 if (loop == 1)
1392 cl->class_class_function_count_child = gap->ga_len;
1393 else
1394 cl->class_obj_method_count_child = gap->ga_len;
1395
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001396 if (loop == 2)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001397 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001398 // Copy instance methods from the parent.
1399
1400 for (int i = 0; i < parent_count; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001401 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001402 // Can't use the same parent function, because "uf_class" is
1403 // different and compilation will have a different result.
1404 // Put them after the functions in the current class, object
1405 // methods may be overruled, then "super.Method()" is used to
1406 // find a method from the parent.
1407 ufunc_T *pf = (extends_cl->class_obj_methods)[i];
1408 (*fup)[gap->ga_len + i] = copy_function(pf);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001409
1410 // If the child class overrides a function from the parent
1411 // the signature must be equal.
1412 char_u *pname = pf->uf_name;
1413 for (int ci = 0; ci < gap->ga_len; ++ci)
1414 {
1415 ufunc_T *cf = (*fup)[ci];
1416 char_u *cname = cf->uf_name;
1417 if (STRCMP(pname, cname) == 0)
1418 {
1419 where_T where = WHERE_INIT;
1420 where.wt_func_name = (char *)pname;
1421 where.wt_kind = WT_METHOD;
1422 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1423 TRUE, where);
1424 }
1425 }
1426 }
1427 }
1428
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001429 // Set the class pointer on all the functions and object methods.
1430 for (int i = 0; i < *fcount; ++i)
1431 {
1432 ufunc_T *fp = (*fup)[i];
1433 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001434 if (i < gap->ga_len)
1435 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001436 if (loop == 2)
1437 fp->uf_flags |= FC_OBJECT;
1438 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001439
1440 ga_clear(gap);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001441 }
1442
1443 return OK;
1444}
1445
1446/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001447 * Update the index of object methods called by builtin functions.
1448 */
1449 static void
1450update_builtin_method_index(class_T *cl)
1451{
1452 int i;
1453
1454 for (i = 0; i < CLASS_BUILTIN_MAX; i++)
1455 cl->class_builtin_methods[i] = -1;
1456
1457 for (i = 0; i < cl->class_obj_method_count; i++)
1458 {
1459 ufunc_T *uf = cl->class_obj_methods[i];
1460
1461 if (cl->class_builtin_methods[CLASS_BUILTIN_STRING] == -1
1462 && STRCMP(uf->uf_name, "string") == 0)
1463 cl->class_builtin_methods[CLASS_BUILTIN_STRING] = i;
1464 else if (cl->class_builtin_methods[CLASS_BUILTIN_EMPTY] == -1 &&
1465 STRCMP(uf->uf_name, "empty") == 0)
1466 cl->class_builtin_methods[CLASS_BUILTIN_EMPTY] = i;
1467 else if (cl->class_builtin_methods[CLASS_BUILTIN_LEN] == -1 &&
1468 STRCMP(uf->uf_name, "len") == 0)
1469 cl->class_builtin_methods[CLASS_BUILTIN_LEN] = i;
1470 }
1471}
1472
1473/*
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001474 * Return the end of the class name starting at "arg". Valid characters in a
1475 * class name are alphanumeric characters and "_". Also handles imported class
1476 * names.
1477 */
1478 static char_u *
1479find_class_name_end(char_u *arg)
1480{
1481 char_u *end = arg;
1482
1483 while (ASCII_ISALNUM(*end) || *end == '_'
1484 || (*end == '.' && (ASCII_ISALNUM(end[1]) || end[1] == '_')))
1485 ++end;
1486
1487 return end;
1488}
1489
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001490/*
1491 * Returns TRUE if the enum value "varname" is already defined.
1492 */
1493 static int
1494is_duplicate_enum(
1495 garray_T *enum_gap,
1496 char_u *varname,
1497 char_u *varname_end)
1498{
1499 char_u *name = vim_strnsave(varname, varname_end - varname);
1500 int dup = FALSE;
1501
1502 for (int i = 0; i < enum_gap->ga_len; ++i)
1503 {
1504 ocmember_T *m = ((ocmember_T *)enum_gap->ga_data) + i;
1505 if (STRCMP(name, m->ocm_name) == 0)
1506 {
1507 semsg(_(e_duplicate_enum_str), name);
1508 dup = TRUE;
1509 break;
1510 }
1511 }
1512
1513 vim_free(name);
1514 return dup;
1515}
1516
1517/*
1518 * Parse the enum values in "line" separated by comma and add them to "gap".
1519 * If the last enum value is found, then "enum_end" is set to TRUE.
1520 */
1521 static int
1522enum_parse_values(
1523 exarg_T *eap,
1524 class_T *en,
1525 char_u *line,
1526 garray_T *gap,
1527 int *num_enum_values,
1528 int *enum_end)
1529{
1530 evalarg_T evalarg;
1531 char_u *p = line;
1532 char initexpr_buf[1024];
1533 char_u last_char = NUL;
1534 int rc = OK;
1535
1536 fill_evalarg_from_eap(&evalarg, eap, FALSE);
1537
1538 int did_emsg_before = did_emsg;
1539 while (*p != NUL)
1540 {
1541 // ignore comment
1542 if (*p == '#')
1543 break;
1544
1545 if (!eval_isnamec1(*p))
1546 {
1547 semsg(_(e_invalid_enum_value_declaration_str), p);
1548 break;
1549 }
1550
1551 char_u *eni_name_start = p;
1552 char_u *eni_name_end = to_name_end(p, FALSE);
1553
1554 if (is_duplicate_enum(gap, eni_name_start, eni_name_end))
1555 break;
1556
1557 p = skipwhite(eni_name_end);
1558
1559 char_u *init_expr = NULL;
1560 if (*p == '(')
1561 {
1562 if (VIM_ISWHITE(p[-1]))
1563 {
1564 semsg(_(e_no_white_space_allowed_before_str_str), "(", line);
1565 break;
1566 }
1567
1568 char_u *expr_start, *expr_end;
1569
1570 p = eni_name_start;
1571 (void)skip_expr_concatenate(&p, &expr_start, &expr_end, &evalarg);
1572
1573 while (*expr_start && *expr_start != '(')
1574 expr_start++;
1575
1576 if (expr_end > expr_start)
1577 init_expr = vim_strnsave(expr_start, expr_end - expr_start);
1578 }
1579
1580 if (init_expr == NULL)
1581 vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new()",
1582 en->class_name);
1583 else
1584 {
1585 vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new%s",
1586 en->class_name, init_expr);
1587 vim_free(init_expr);
1588 }
1589 if (add_member(gap, eni_name_start, eni_name_end, FALSE,
1590 TRUE, TRUE, TRUE, &en->class_object_type,
1591 vim_strsave((char_u *)initexpr_buf)) == FAIL)
1592 break;
1593
1594 ++*num_enum_values;
1595
1596 if (*p != '#')
1597 last_char = *p;
1598
1599 if (*p != NUL && *p != ',')
1600 break;
1601
1602 if (*p == ',')
1603 {
1604 if (!IS_WHITE_OR_NUL(p[1]))
1605 {
1606 semsg(_(e_white_space_required_after_str_str), ",", line);
1607 break;
1608 }
1609 if (VIM_ISWHITE(p[-1]))
1610 {
1611 semsg(_(e_no_white_space_allowed_before_str_str), ",", line);
1612 break;
1613 }
1614 p = skipwhite(p + 1);
1615 }
1616 }
1617
1618 if (*p != NUL && *p != '#')
1619 {
1620 if (did_emsg == did_emsg_before)
1621 semsg(_(e_missing_comma_before_argument_str), p);
1622 rc = FAIL;
1623 }
1624
1625 if (last_char != ',')
1626 // last enum value should not be terminated by ","
1627 *enum_end = TRUE;
1628
1629 // Free the memory pointed by expr_start.
1630 clear_evalarg(&evalarg, NULL);
1631
1632 return rc;
1633}
1634
1635/*
1636 * Add the "values" class variable (List of enum value objects) to the enum
1637 * class "en"
1638 */
1639 static int
1640enum_add_values_member(
1641 class_T *en,
1642 garray_T *gap,
1643 int num_enum_values,
1644 garray_T *type_list_gap)
1645{
1646 garray_T fga;
1647 int rc = FAIL;
1648
1649 ga_init2(&fga, 1, 1000);
1650 ga_concat(&fga, (char_u *)"[");
1651 for (int i = 0; i < num_enum_values; ++i)
1652 {
1653 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
1654
1655 if (i > 0)
1656 ga_concat(&fga, (char_u *)", ");
1657 ga_concat(&fga, en->class_name);
1658 ga_concat(&fga, (char_u *)".");
1659 ga_concat(&fga, (char_u *)m->ocm_name);
1660 }
1661 ga_concat(&fga, (char_u *)"]");
1662 ga_append(&fga, NUL);
1663
1664 char_u *varname = (char_u *)"values";
1665
1666 type_T *type = get_type_ptr(type_list_gap);
1667 if (type == NULL)
1668 goto done;
1669
1670 type->tt_type = VAR_LIST;
1671 type->tt_member = get_type_ptr(type_list_gap);
1672 if (type->tt_member != NULL)
1673 {
1674 type->tt_member->tt_type = VAR_OBJECT;
1675 type->tt_member->tt_class = en;
1676 }
1677
1678 rc = add_member(gap, varname, varname + 6, FALSE, FALSE, TRUE, TRUE, type,
1679 vim_strsave((char_u *)fga.ga_data));
1680
1681done:
1682 vim_free(fga.ga_data);
1683
1684 return rc;
1685}
1686
1687/*
1688 * Clear the constructor method names in a enum class, so that an enum class
1689 * cannot be instantiated.
1690 */
1691 static void
1692enum_clear_constructors(class_T *en)
1693{
1694 for (int i = 0; i < en->class_class_function_count; ++i)
1695 {
1696 ufunc_T *fp = en->class_class_functions[i];
1697
1698 if (fp->uf_flags & FC_NEW)
1699 *fp->uf_name = NUL;
1700 }
1701}
1702
1703/*
1704 * Initialize the name and ordinal object variable in the enum value "enval" in
1705 * the enum "en". These values are set during the enum value object creation.
1706 */
1707 void
1708enum_set_internal_obj_vars(class_T *en, object_T *enval)
1709{
1710 int i;
1711
1712 for (i = 0; i < en->class_class_member_count; ++i)
1713 {
1714 typval_T *en_tv = en->class_members_tv + i;
1715 if (en_tv != NULL && en_tv->v_type == VAR_UNKNOWN)
1716 break;
1717 }
1718
1719 // First object variable is the name
1720 ocmember_T *value_ocm = en->class_class_members + i;
1721 typval_T *name_tv = (typval_T *)(enval + 1);
1722 name_tv->v_type = VAR_STRING;
1723 name_tv->vval.v_string = vim_strsave(value_ocm->ocm_name);
1724
1725 // Second object variable is the ordinal
1726 typval_T *ord_tv = (typval_T *)(name_tv + 1);
1727 ord_tv->v_type = VAR_NUMBER;
1728 ord_tv->vval.v_number = i;
1729}
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001730
1731/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001732 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001733 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001734 */
1735 void
1736ex_class(exarg_T *eap)
1737{
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001738 int is_class = eap->cmdidx == CMD_class;
1739 int is_abstract = eap->cmdidx == CMD_abstract;
1740 int is_enum = eap->cmdidx == CMD_enum;
1741 int is_interface;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001742 long start_lnum = SOURCING_LNUM;
1743 char_u *arg = eap->arg;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001744
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001745 if (is_abstract)
1746 {
1747 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1748 {
1749 semsg(_(e_invalid_argument_str), arg);
1750 return;
1751 }
1752 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001753 is_class = TRUE;
1754 }
1755
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001756 is_interface = !is_class && !is_enum;
1757
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001758 if (!current_script_is_vim9()
1759 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001760 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001761 {
1762 if (is_class)
1763 emsg(_(e_class_can_only_be_defined_in_vim9_script));
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001764 else if (is_enum)
1765 emsg(_(e_enum_can_only_be_defined_in_vim9_script));
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001766 else
1767 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1768 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001769 }
1770
1771 if (!ASCII_ISUPPER(*arg))
1772 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001773 if (is_class)
1774 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001775 else if (is_enum)
1776 semsg(_(e_enum_name_must_start_with_uppercase_letter_str), arg);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001777 else
1778 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1779 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001780 return;
1781 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001782 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1783 if (!IS_WHITE_OR_NUL(*name_end))
1784 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001785 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001786 return;
1787 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001788 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001789
1790 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001791 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001792
Bram Moolenaar83677162023-01-08 19:54:10 +00001793 // Name for "extends BaseClass"
1794 char_u *extends = NULL;
1795
Bram Moolenaar94674f22023-01-06 18:42:20 +00001796 // Names for "implements SomeInterface"
1797 garray_T ga_impl;
1798 ga_init2(&ga_impl, sizeof(char_u *), 5);
1799
1800 arg = skipwhite(name_end);
1801 while (*arg != NUL && *arg != '#' && *arg != '\n')
1802 {
1803 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001804 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001805 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1806 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001807 if (is_enum)
1808 {
1809 emsg(_(e_enum_cannot_extend_class));
1810 goto early_ret;
1811 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001812 if (extends != NULL)
1813 {
1814 emsg(_(e_duplicate_extends));
1815 goto early_ret;
1816 }
1817 arg = skipwhite(arg + 7);
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001818
1819 char_u *end = find_class_name_end(arg);
Bram Moolenaar83677162023-01-08 19:54:10 +00001820 if (!IS_WHITE_OR_NUL(*end))
1821 {
1822 semsg(_(e_white_space_required_after_name_str), arg);
1823 goto early_ret;
1824 }
1825 extends = vim_strnsave(arg, end - arg);
1826 if (extends == NULL)
1827 goto early_ret;
1828
1829 arg = skipwhite(end + 1);
1830 }
1831 else if (STRNCMP(arg, "implements", 10) == 0
1832 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001833 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001834 if (is_interface)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001835 {
1836 emsg(_(e_interface_cannot_use_implements));
1837 goto early_ret;
1838 }
1839
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001840 if (ga_impl.ga_len > 0)
1841 {
1842 emsg(_(e_duplicate_implements));
1843 goto early_ret;
1844 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001845 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001846
1847 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001848 {
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001849 char_u *impl_end = find_class_name_end(arg);
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001850 if ((!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1851 || (*impl_end == ','
1852 && !IS_WHITE_OR_NUL(*(impl_end + 1))))
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001853 {
1854 semsg(_(e_white_space_required_after_name_str), arg);
1855 goto early_ret;
1856 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001857 if (impl_end - arg == 0)
1858 {
1859 emsg(_(e_missing_name_after_implements));
1860 goto early_ret;
1861 }
1862
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001863 char_u *iname = vim_strnsave(arg, impl_end - arg);
1864 if (iname == NULL)
1865 goto early_ret;
1866 for (int i = 0; i < ga_impl.ga_len; ++i)
1867 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1868 {
1869 semsg(_(e_duplicate_interface_after_implements_str),
1870 iname);
1871 vim_free(iname);
1872 goto early_ret;
1873 }
1874 if (ga_add_string(&ga_impl, iname) == FAIL)
1875 {
1876 vim_free(iname);
1877 goto early_ret;
1878 }
1879 if (*impl_end != ',')
1880 {
1881 arg = skipwhite(impl_end);
1882 break;
1883 }
1884 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001885 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001886 }
1887 else
1888 {
1889 semsg(_(e_trailing_characters_str), arg);
1890early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001891 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001892 ga_clear_strings(&ga_impl);
1893 return;
1894 }
1895 }
1896
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001897 garray_T type_list; // list of pointers to allocated types
1898 ga_init2(&type_list, sizeof(type_T *), 10);
1899
Bram Moolenaard505d172022-12-18 21:42:55 +00001900 // Growarray with class members declared in the class.
1901 garray_T classmembers;
1902 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1903
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001904 // Growarray with functions declared in the class.
1905 garray_T classfunctions;
1906 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001907
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001908 // Growarray with object members declared in the class.
1909 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001910 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001911
1912 // Growarray with object methods declared in the class.
1913 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001914 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001915
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01001916 class_T *cl = NULL;
1917 class_T *extends_cl = NULL; // class from "extends" argument
1918 class_T **intf_classes = NULL;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001919 int num_enum_values = 0;
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01001920
1921 cl = ALLOC_CLEAR_ONE(class_T);
1922 if (cl == NULL)
1923 goto cleanup;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001924
1925 if (is_enum)
1926 cl->class_flags = CLASS_ENUM;
1927 else if (is_interface)
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01001928 cl->class_flags = CLASS_INTERFACE;
1929 else if (is_abstract)
1930 cl->class_flags = CLASS_ABSTRACT;
1931
1932 cl->class_refcount = 1;
1933 cl->class_name = vim_strnsave(name_start, name_end - name_start);
1934 if (cl->class_name == NULL)
1935 goto cleanup;
1936
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001937 cl->class_type.tt_type = VAR_CLASS;
1938 cl->class_type.tt_class = cl;
1939 cl->class_object_type.tt_type = VAR_OBJECT;
1940 cl->class_object_type.tt_class = cl;
1941
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01001942 // Add the class to the script-local variables.
1943 // TODO: handle other context, e.g. in a function
1944 // TODO: does uf_hash need to be cleared?
1945 typval_T tv;
1946 tv.v_type = VAR_CLASS;
1947 tv.vval.v_class = cl;
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01001948 SOURCING_LNUM = start_lnum;
1949 int rc = set_var_const(cl->class_name, current_sctx.sc_sid,
1950 NULL, &tv, FALSE, 0, 0);
1951 if (rc == FAIL)
1952 goto cleanup;
1953
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001954 if (is_enum)
1955 {
1956 // All the enum classes have the name and ordinal object variables.
1957 char_u *varname = (char_u *)"name";
1958 if (add_member(&objmembers, varname, varname + 4, FALSE, FALSE, TRUE,
1959 TRUE, &t_string, NULL) == FAIL)
1960 goto cleanup;
1961
1962 varname = (char_u *)"ordinal";
1963 if (add_member(&objmembers, varname, varname + 7, FALSE, FALSE, TRUE,
1964 TRUE, &t_number, NULL) == FAIL)
1965 goto cleanup;
1966 }
1967
1968 // "export class" gets used when creating the class, don't use "is_export"
1969 // for the items inside the class.
1970 is_export = FALSE;
1971
1972 // When parsing an enum definition, this denotes whether all the enumerated
1973 // values are parsed or not.
1974 int enum_end = FALSE;
1975
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001976 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001977 * Go over the body of the class/interface until "endclass" or
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01001978 * "endinterface" or "endenum" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001979 */
1980 char_u *theline = NULL;
1981 int success = FALSE;
1982 for (;;)
1983 {
1984 vim_free(theline);
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001985 theline = eap->ea_getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001986 if (theline == NULL)
1987 break;
1988 char_u *line = skipwhite(theline);
1989
Bram Moolenaar418b5472022-12-20 13:38:22 +00001990 // Skip empty and comment lines.
1991 if (*line == NUL)
1992 continue;
1993 if (*line == '#')
1994 {
1995 if (vim9_bad_comment(line))
1996 break;
1997 continue;
1998 }
1999
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002000 char_u *p = line;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002001
2002 char *end_name;
2003 int shortlen;
2004 int fullen;
2005 if (is_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002006 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002007 end_name = "endclass";
2008 shortlen = 4;
2009 fullen = 8;
2010 }
2011 else if (is_enum)
2012 {
2013 end_name = "endenum";
2014 shortlen = 4;
2015 fullen = 7;
2016 }
2017 else
2018 {
2019 end_name = "endinterface";
2020 shortlen = 5;
2021 fullen = 12;
2022 }
2023
2024 if (checkforcmd(&p, end_name, shortlen))
2025 {
2026 if (STRNCMP(line, end_name, fullen) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002027 semsg(_(e_command_cannot_be_shortened_str), line);
2028 else if (*p == '|' || !ends_excmd2(line, p))
2029 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00002030 else
2031 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002032 break;
2033 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002034
2035 int wrong_endname = FALSE;
2036 if (is_class)
2037 wrong_endname = checkforcmd(&p, "endinterface", 5)
2038 || checkforcmd(&p, "endenum", 4);
2039 else if (is_enum)
2040 wrong_endname = checkforcmd(&p, "endclass", 4)
2041 || checkforcmd(&p, "endinterface", 5);
2042 else
2043 wrong_endname = checkforcmd(&p, "endclass", 4)
2044 || checkforcmd(&p, "endenum", 4);
2045 if (wrong_endname)
Bram Moolenaar554d0312023-01-05 19:59:18 +00002046 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00002047 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00002048 break;
2049 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002050
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002051 if (is_enum && !enum_end)
2052 {
2053 // In an enum, all the enumerated values are at the beginning
2054 // separated by comma. The class and object variables/methods
2055 // follow the values.
2056 if (enum_parse_values(eap, cl, line, &classmembers,
2057 &num_enum_values, &enum_end) == FAIL)
2058 break;
2059 continue;
2060 }
2061
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002062 int has_public = FALSE;
2063 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002064 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002065 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002066 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002067 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002068 break;
2069 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002070 if (is_interface)
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002071 {
Yegappan Lakshmananb90e3bc2023-09-28 23:06:48 +02002072 emsg(_(e_public_variable_not_supported_in_interface));
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002073 break;
2074 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002075 has_public = TRUE;
2076 p = skipwhite(line + 6);
2077
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002078 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "static", 6) != 0
2079 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002080 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002081 emsg(_(e_public_must_be_followed_by_var_static_final_or_const));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002082 break;
2083 }
2084 }
Bram Moolenaard505d172022-12-18 21:42:55 +00002085
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002086 int abstract_method = FALSE;
2087 char_u *pa = p;
2088 if (checkforcmd(&p, "abstract", 3))
2089 {
2090 if (STRNCMP(pa, "abstract", 8) != 0)
2091 {
2092 semsg(_(e_command_cannot_be_shortened_str), pa);
2093 break;
2094 }
2095
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002096 if (is_enum)
2097 {
2098 // "abstract" not supported in an enum
2099 emsg(_(e_abstract_cannot_be_used_in_enum));
2100 break;
2101 }
2102
2103 if (is_interface)
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01002104 {
2105 // "abstract" not supported in an interface
2106 emsg(_(e_abstract_cannot_be_used_in_interface));
2107 break;
2108 }
2109
2110 if (!is_abstract)
2111 {
2112 semsg(_(e_abstract_method_in_concrete_class), pa);
2113 break;
2114 }
2115
Yegappan Lakshmanan5a539252023-11-04 09:42:46 +01002116 p = skipwhite(pa + 8);
2117 if (STRNCMP(p, "def", 3) != 0)
2118 {
2119 emsg(_(e_abstract_must_be_followed_by_def));
2120 break;
2121 }
2122
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01002123 abstract_method = TRUE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002124 }
2125
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002126 int has_static = FALSE;
2127 char_u *ps = p;
2128 if (checkforcmd(&p, "static", 4))
2129 {
2130 if (STRNCMP(ps, "static", 6) != 0)
2131 {
2132 semsg(_(e_command_cannot_be_shortened_str), ps);
2133 break;
2134 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002135
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002136 if (is_interface)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002137 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002138 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002139 break;
2140 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002141 has_static = TRUE;
2142 p = skipwhite(ps + 6);
Doug Kearns74da0ee2023-12-14 20:26:26 +01002143
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002144 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "def", 3) != 0
2145 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Doug Kearns74da0ee2023-12-14 20:26:26 +01002146 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002147 emsg(_(e_static_must_be_followed_by_var_def_final_or_const));
Doug Kearns74da0ee2023-12-14 20:26:26 +01002148 break;
2149 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002150 }
2151
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002152 int has_final = FALSE;
2153 int has_var = FALSE;
2154 int has_const = FALSE;
2155 if (checkforcmd(&p, "var", 3))
2156 has_var = TRUE;
2157 else if (checkforcmd(&p, "final", 5))
2158 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002159 if (is_interface)
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002160 {
2161 emsg(_(e_final_variable_not_supported_in_interface));
2162 break;
2163 }
2164 has_final = TRUE;
2165 }
2166 else if (checkforcmd(&p, "const", 5))
2167 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002168 if (is_interface)
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002169 {
2170 emsg(_(e_const_variable_not_supported_in_interface));
2171 break;
2172 }
2173 has_const = TRUE;
2174 }
2175 p = skipwhite(p);
2176
Bram Moolenaard505d172022-12-18 21:42:55 +00002177 // object members (public, read access, private):
Doug Kearns74da0ee2023-12-14 20:26:26 +01002178 // "var _varname"
2179 // "var varname"
2180 // "public var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002181 // "final _varname"
2182 // "final varname"
2183 // "public final varname"
2184 // "const _varname"
2185 // "const varname"
2186 // "public const varname"
Doug Kearns74da0ee2023-12-14 20:26:26 +01002187 // class members (public, read access, private):
2188 // "static var _varname"
2189 // "static var varname"
2190 // "public static var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002191 // "static final _varname"
2192 // "static final varname"
2193 // "public static final varname"
2194 // "static const _varname"
2195 // "static const varname"
2196 // "public static const varname"
2197 if (has_var || has_final || has_const)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002198 {
Doug Kearns74da0ee2023-12-14 20:26:26 +01002199 char_u *varname = p;
Bram Moolenaard505d172022-12-18 21:42:55 +00002200 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00002201 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00002202 char_u *init_expr = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002203 int has_type = FALSE;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002204
Doug Kearns74da0ee2023-12-14 20:26:26 +01002205 if (!eval_isnamec1(*p))
2206 {
2207 if (has_static)
2208 semsg(_(e_invalid_class_variable_declaration_str), line);
2209 else
2210 semsg(_(e_invalid_object_variable_declaration_str), line);
2211 break;
2212 }
2213
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002214 if (is_interface && *varname == '_')
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002215 {
2216 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01002217 semsg(_(e_protected_variable_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002218 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002219 break;
2220 }
2221
Bram Moolenaard505d172022-12-18 21:42:55 +00002222 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002223 &varname_end, &has_type, &type_list, &type,
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002224 !is_interface ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002225 break;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002226
2227 if (is_reserved_varname(varname, varname_end)
2228 || is_duplicate_variable(&classmembers, &objmembers,
2229 varname, varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02002230 {
2231 vim_free(init_expr);
2232 break;
2233 }
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002234 if (add_member(has_static ? &classmembers : &objmembers, varname,
2235 varname_end, has_public, has_final, has_const,
2236 has_type, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00002237 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002238 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002239 break;
2240 }
Bram Moolenaard505d172022-12-18 21:42:55 +00002241 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002242
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002243 // constructors:
2244 // def new()
2245 // enddef
2246 // def newOther()
2247 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002248 // object methods and class functions:
2249 // def SomeMethod()
2250 // enddef
2251 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002252 // enddef
2253 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002254 // def <Tval> someMethod()
2255 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002256 else if (checkforcmd(&p, "def", 3))
2257 {
2258 exarg_T ea;
2259 garray_T lines_to_free;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002260 int is_new = STRNCMP(p, "new", 3) == 0;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002261
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002262 if (has_public)
2263 {
2264 // "public" keyword is not supported when defining an object or
2265 // class method
2266 emsg(_(e_public_keyword_not_supported_for_method));
2267 break;
2268 }
2269
2270 if (*p == NUL)
2271 {
2272 // No method name following def
2273 semsg(_(e_not_valid_command_in_class_str), line);
2274 break;
2275 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002276
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002277 if (is_interface && *p == '_')
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01002278 {
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002279 // private methods are not supported in an interface
2280 semsg(_(e_protected_method_not_supported_in_interface), p);
2281 break;
2282 }
2283
2284 if (has_static && !is_new && SAFE_islower(*p) &&
2285 is_valid_builtin_obj_methodname(p))
2286 {
2287 semsg(_(e_builtin_class_method_not_supported), p);
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01002288 break;
2289 }
2290
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002291 CLEAR_FIELD(ea);
2292 ea.cmd = line;
2293 ea.arg = p;
2294 ea.cmdidx = CMD_def;
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002295 ea.ea_getline = eap->ea_getline;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002296 ea.cookie = eap->cookie;
2297
2298 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002299 int class_flags;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002300 if (is_interface)
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002301 class_flags = CF_INTERFACE;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002302 else
2303 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
Bram Moolenaar554d0312023-01-05 19:59:18 +00002304 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02002305 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002306 ga_clear_strings(&lines_to_free);
2307
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002308 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002309 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002310 char_u *name = uf->uf_name;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02002311
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002312 if (is_new && !is_valid_constructor(uf, is_abstract,
2313 has_static))
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002314 {
2315 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01002316 semsg(_(e_protected_method_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002317 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002318 func_clear_free(uf, FALSE);
2319 break;
2320 }
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002321
2322 // check for builtin method
2323 if (!is_new && SAFE_islower(*name) &&
2324 !object_check_builtin_method_sig(uf))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00002325 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02002326 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00002327 break;
2328 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02002329
Bram Moolenaar58b40092023-01-11 15:59:05 +00002330 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002331 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002332 {
2333 success = FALSE;
2334 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02002335 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002336 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00002337
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002338 garray_T *fgap = has_static || is_new
2339 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002340 if (ga_grow(fgap, 1) == OK)
2341 {
2342 if (is_new)
2343 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002344
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002345 if (abstract_method)
2346 uf->uf_flags |= FC_ABSTRACT;
2347
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002348 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
2349 ++fgap->ga_len;
2350 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002351 }
2352 }
2353
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002354 else
2355 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00002356 if (is_class)
2357 semsg(_(e_not_valid_command_in_class_str), line);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002358 else if (is_enum)
2359 semsg(_(e_not_valid_command_in_enum_str), line);
Bram Moolenaar554d0312023-01-05 19:59:18 +00002360 else
2361 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002362 break;
2363 }
2364 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002365
2366 if (theline == NULL && !success && is_enum)
2367 emsg(_(e_missing_endenum));
2368
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002369 vim_free(theline);
2370
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002371 if (success && is_enum)
2372 // Add the enum "values" class variable.
2373 enum_add_values_member(cl, &classmembers, num_enum_values, &type_list);
2374
Bram Moolenaar83677162023-01-08 19:54:10 +00002375 /*
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002376 * Check a few things
Bram Moolenaar83677162023-01-08 19:54:10 +00002377 */
2378
2379 // Check the "extends" class is valid.
2380 if (success && extends != NULL)
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002381 success = validate_extends_class(cl, extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00002382 VIM_CLEAR(extends);
2383
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002384 // Check the new object methods to make sure their access (public or
2385 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002386 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002387 success = validate_extends_methods(&objmethods, extends_cl);
2388
2389 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002390 // variables in the extended class lineage. If an interface is extending
2391 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002392 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002393 {
2394 if (is_class)
2395 success = extends_check_dup_members(&objmembers, extends_cl);
2396 else
2397 success = extends_check_intf_var_type(&objmembers, extends_cl);
2398 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002399
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002400 // When extending an abstract class, make sure all the abstract methods in
2401 // the parent class are implemented. If the current class is an abstract
2402 // class, then there is no need for this check.
2403 if (success && !is_abstract && extends_cl != NULL
2404 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002405 success = validate_abstract_class_methods(&classfunctions,
2406 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002407
Bram Moolenaar83677162023-01-08 19:54:10 +00002408 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00002409 if (success && ga_impl.ga_len > 0)
2410 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002411 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
2412
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002413 success = validate_implements_classes(&ga_impl, intf_classes,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002414 &objmethods, &objmembers, extends_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00002415 }
2416
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002417 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00002418 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002419 success = check_func_arg_names(&classfunctions, &objmethods,
2420 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00002421
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002422 if (success)
2423 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002424 // "endclass" or "endinterface" or "endenum" encountered without any
2425 // failures
Bram Moolenaard505d172022-12-18 21:42:55 +00002426
Bram Moolenaard0200c82023-01-28 15:19:40 +00002427 if (extends_cl != NULL)
2428 {
2429 cl->class_extends = extends_cl;
2430 extends_cl->class_flags |= CLASS_EXTENDED;
2431 }
Bram Moolenaar83677162023-01-08 19:54:10 +00002432
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002433 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00002434 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002435 NULL,
2436 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00002437 &cl->class_class_members,
2438 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00002439 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00002440 extends_cl == NULL ? NULL
2441 : extends_cl->class_obj_members,
2442 extends_cl == NULL ? 0
2443 : extends_cl->class_obj_member_count,
2444 &cl->class_obj_members,
2445 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002446 goto cleanup;
2447
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00002448 if (ga_impl.ga_len > 0)
2449 {
2450 // Move the "implements" names into the class.
2451 cl->class_interface_count = ga_impl.ga_len;
2452 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
2453 if (cl->class_interfaces == NULL)
2454 goto cleanup;
2455 for (int i = 0; i < ga_impl.ga_len; ++i)
2456 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
2457 VIM_CLEAR(ga_impl.ga_data);
2458 ga_impl.ga_len = 0;
2459
Bram Moolenaard0200c82023-01-28 15:19:40 +00002460 cl->class_interfaces_cl = intf_classes;
2461 intf_classes = NULL;
2462 }
2463
2464 if (cl->class_interface_count > 0 || extends_cl != NULL)
2465 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002466 // Add a method and member lookup table to each of the interface
2467 // classes.
2468 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
2469 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00002470 }
2471
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002472 int have_new = FALSE;
2473 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002474 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002475 {
2476 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
2477 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002478 {
2479 have_new = TRUE;
2480 break;
2481 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002482 }
2483
2484 if (have_new)
2485 // The return type of new() is an object of class "cl"
2486 class_func->uf_ret_type->tt_class = cl;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002487 else if ((is_class || is_enum) && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002488 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002489 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002490
Bram Moolenaar58b40092023-01-11 15:59:05 +00002491 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002492 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
2493 &objmethods) == FAIL)
2494 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002495
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002496 update_builtin_method_index(cl);
2497
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002498 class_created(cl);
2499
2500 // Allocate a typval for each class member and initialize it.
2501 if ((is_class || is_enum) && cl->class_class_member_count > 0)
2502 if (add_class_members(cl, eap, &type_list) == FAIL)
2503 goto cleanup;
2504
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002505 cl->class_type_list = type_list;
2506
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002507 if (is_enum)
2508 {
2509 // clear the constructor method names, so that an enum class cannot
2510 // be instantiated
2511 enum_clear_constructors(cl);
2512 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002513
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002514 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00002515 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002516
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002517 return;
2518 }
2519
2520cleanup:
Bram Moolenaar83677162023-01-08 19:54:10 +00002521 vim_free(extends);
2522 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002523
2524 if (intf_classes != NULL)
2525 {
2526 for (int i = 0; i < ga_impl.ga_len; ++i)
2527 class_unref(intf_classes[i]);
2528 vim_free(intf_classes);
2529 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00002530 ga_clear_strings(&ga_impl);
2531
Bram Moolenaard505d172022-12-18 21:42:55 +00002532 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002533 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002534 garray_T *gap = round == 1 ? &classmembers : &objmembers;
2535 if (gap->ga_len == 0 || gap->ga_data == NULL)
2536 continue;
2537
2538 for (int i = 0; i < gap->ga_len; ++i)
2539 {
2540 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2541 vim_free(m->ocm_name);
2542 vim_free(m->ocm_init);
2543 }
2544 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002545 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002546
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002547 for (int i = 0; i < objmethods.ga_len; ++i)
2548 {
2549 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2550 func_clear_free(uf, FALSE);
2551 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002552 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002553
2554 for (int i = 0; i < classfunctions.ga_len; ++i)
2555 {
2556 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2557 func_clear_free(uf, FALSE);
2558 }
2559 ga_clear(&classfunctions);
2560
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002561 clear_type_list(&type_list);
2562}
2563
2564/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002565 * Find member "name" in class "cl", set "member_idx" to the member index and
2566 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002567 * When "is_object" is TRUE, then look for object members. Otherwise look for
2568 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002569 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002570 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002571 */
2572 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002573oc_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002574 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002575 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002576 char_u *name,
2577 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002578 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002579{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002580 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002581 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002582
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002583 *member_idx = -1; // not found (yet)
2584
2585 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2586 member_idx);
2587 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002588 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002589 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2590 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002591 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002592 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002593
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002594 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002595}
2596
2597/*
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002598 * Given a class or object variable index, return the variable type
2599 */
2600 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002601oc_member_type_by_idx(
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002602 class_T *cl,
2603 int is_object,
2604 int member_idx)
2605{
2606 ocmember_T *m;
2607 int member_count;
2608
2609 if (is_object)
2610 {
2611 m = cl->class_obj_members;
2612 member_count = cl->class_obj_member_count;
2613 }
2614 else
2615 {
2616 m = cl->class_class_members;
2617 member_count = cl->class_class_member_count;
2618 }
2619
2620 if (member_idx >= member_count)
2621 return NULL;
2622
2623 return m[member_idx].ocm_type;
2624}
2625
2626/*
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002627 * Type aliases (:type)
2628 */
2629
2630 void
2631typealias_free(typealias_T *ta)
2632{
2633 // ta->ta_type is freed in clear_type_list()
2634 vim_free(ta->ta_name);
2635 vim_free(ta);
2636}
2637
2638 void
2639typealias_unref(typealias_T *ta)
2640{
2641 if (ta != NULL && --ta->ta_refcount <= 0)
2642 typealias_free(ta);
2643}
2644
2645/*
2646 * Handle ":type". Create an alias for a type specification.
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002647 */
2648 void
2649ex_type(exarg_T *eap UNUSED)
2650{
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002651 char_u *arg = eap->arg;
2652
2653 if (!current_script_is_vim9()
2654 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002655 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002656 {
2657 emsg(_(e_type_can_only_be_defined_in_vim9_script));
2658 return;
2659 }
2660
2661 if (*arg == NUL)
2662 {
2663 emsg(_(e_missing_typealias_name));
2664 return;
2665 }
2666
2667 if (!ASCII_ISUPPER(*arg))
2668 {
2669 semsg(_(e_type_name_must_start_with_uppercase_letter_str), arg);
2670 return;
2671 }
2672
2673 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
2674 if (!IS_WHITE_OR_NUL(*name_end))
2675 {
2676 semsg(_(e_white_space_required_after_name_str), arg);
2677 return;
2678 }
2679 char_u *name_start = arg;
2680
2681 arg = skipwhite(name_end);
2682 if (*arg != '=')
2683 {
2684 semsg(_(e_missing_equal_str), arg);
2685 return;
2686 }
2687 if (!IS_WHITE_OR_NUL(*(arg + 1)))
2688 {
2689 semsg(_(e_white_space_required_after_str_str), "=", arg);
2690 return;
2691 }
2692 arg++;
2693 arg = skipwhite(arg);
2694
2695 if (*arg == NUL)
2696 {
2697 emsg(_(e_missing_typealias_type));
2698 return;
2699 }
2700
2701 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
2702 type_T *type = parse_type(&arg, &si->sn_type_list, TRUE);
2703 if (type == NULL)
2704 return;
2705
2706 if (*arg != NUL)
2707 {
2708 // some text after the type
2709 semsg(_(e_trailing_characters_str), arg);
2710 return;
2711 }
2712
2713 int cc = *name_end;
2714 *name_end = NUL;
2715
2716 typval_T tv;
2717 tv.v_type = VAR_UNKNOWN;
2718 if (eval_variable_import(name_start, &tv) == OK)
2719 {
2720 if (tv.v_type == VAR_TYPEALIAS)
2721 semsg(_(e_typealias_already_exists_for_str), name_start);
2722 else
2723 semsg(_(e_redefining_script_item_str), name_start);
2724 clear_tv(&tv);
2725 goto done;
2726 }
2727
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02002728 // Create a script-local variable for the type alias.
2729 if (type->tt_type != VAR_OBJECT)
2730 {
2731 tv.v_type = VAR_TYPEALIAS;
2732 tv.v_lock = 0;
2733 tv.vval.v_typealias = ALLOC_CLEAR_ONE(typealias_T);
2734 ++tv.vval.v_typealias->ta_refcount;
2735 tv.vval.v_typealias->ta_name = vim_strsave(name_start);
2736 tv.vval.v_typealias->ta_type = type;
2737 }
2738 else
2739 {
2740 // When creating a type alias for a class, use the class type itself to
2741 // create the type alias variable. This is needed to use the type
2742 // alias to invoke class methods (e.g. new()) and use class variables.
2743 tv.v_type = VAR_CLASS;
2744 tv.v_lock = 0;
2745 tv.vval.v_class = type->tt_class;
2746 ++tv.vval.v_class->class_refcount;
2747 }
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002748 set_var_const(name_start, current_sctx.sc_sid, NULL, &tv, FALSE,
2749 ASSIGN_CONST | ASSIGN_FINAL, 0);
2750
2751done:
2752 *name_end = cc;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002753}
2754
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002755/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002756 * Returns OK if a member variable named "name" is present in the class "cl".
2757 * Otherwise returns FAIL. If found, the member variable typval is set in
2758 * "rettv". If "is_object" is TRUE, then the object member variable table is
2759 * searched. Otherwise the class member variable table is searched.
2760 */
2761 static int
2762get_member_tv(
2763 class_T *cl,
2764 int is_object,
2765 char_u *name,
2766 size_t namelen,
2767 typval_T *rettv)
2768{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002769 ocmember_T *m;
2770 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002771
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002772 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2773 &m_idx);
2774 if (m == NULL)
2775 return FAIL;
2776
2777 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002778 {
Ernie Rael03042a22023-11-11 08:53:32 +01002779 emsg_var_cl_define(e_cannot_access_protected_variable_str,
Ernie Raele6c9aa52023-10-06 19:55:52 +02002780 m->ocm_name, 0, cl);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002781 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002782 }
2783
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002784 if (is_object)
2785 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002786 // The object only contains a pointer to the class, the member values
2787 // array follows right after that.
2788 object_T *obj = rettv->vval.v_object;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002789 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2790 copy_tv(tv, rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002791 object_unref(obj);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002792 }
2793 else
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002794 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002795 copy_tv(&cl->class_members_tv[m_idx], rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002796 class_unref(cl);
2797 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002798
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002799 return OK;
2800}
2801
2802/*
2803 * Call an object or class method "name" in class "cl". The method return
2804 * value is returned in "rettv".
2805 */
2806 static int
2807call_oc_method(
2808 class_T *cl,
2809 char_u *name,
2810 size_t len,
2811 char_u *name_end,
2812 evalarg_T *evalarg,
2813 char_u **arg,
2814 typval_T *rettv)
2815{
2816 ufunc_T *fp;
2817 typval_T argvars[MAX_FUNC_ARGS + 1];
2818 int argcount = 0;
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002819 ocmember_T *ocm = NULL;
2820 int m_idx;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002821
2822 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
2823 if (fp == NULL)
2824 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002825 // could be an object or class funcref variable
2826 ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
2827 if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
2828 {
2829 method_not_found_msg(cl, rettv->v_type, name, len);
2830 return FAIL;
2831 }
2832
2833 if (rettv->v_type == VAR_OBJECT)
2834 {
2835 // funcref object variable
2836 object_T *obj = rettv->vval.v_object;
2837 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2838 copy_tv(tv, rettv);
2839 }
2840 else
2841 // funcref class variable
2842 copy_tv(&cl->class_members_tv[m_idx], rettv);
2843 *arg = name_end;
2844 return OK;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002845 }
2846
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002847 if (ocm == NULL && *fp->uf_name == '_')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002848 {
2849 // Cannot access a private method outside of a class
Ernie Rael03042a22023-11-11 08:53:32 +01002850 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002851 return FAIL;
2852 }
2853
2854 char_u *argp = name_end;
Ernie Raelb077b582023-12-14 20:11:44 +01002855 int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount, FALSE);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002856 if (ret == FAIL)
2857 return FAIL;
2858
2859 funcexe_T funcexe;
2860 CLEAR_FIELD(funcexe);
2861 funcexe.fe_evaluate = TRUE;
2862 if (rettv->v_type == VAR_OBJECT)
2863 {
2864 funcexe.fe_object = rettv->vval.v_object;
2865 ++funcexe.fe_object->obj_refcount;
2866 }
2867
2868 // Clear the class or object after calling the function, in
2869 // case the refcount is one.
2870 typval_T tv_tofree = *rettv;
2871 rettv->v_type = VAR_UNKNOWN;
2872
2873 // Call the user function. Result goes into rettv;
2874 int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
2875 NULL);
2876
2877 // Clear the previous rettv and the arguments.
2878 clear_tv(&tv_tofree);
2879 for (int idx = 0; idx < argcount; ++idx)
2880 clear_tv(&argvars[idx]);
2881
2882 if (error != FCERR_NONE)
2883 {
2884 user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
2885 return FAIL;
2886 }
2887 *arg = argp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002888
2889 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002890}
2891
2892/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002893 * Evaluate what comes after a class:
2894 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002895 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002896 * - class constructor: SomeClass.new()
2897 * - object member: someObject.varname
2898 * - object method: someObject.SomeMethod()
2899 *
2900 * "*arg" points to the '.'.
2901 * "*arg" is advanced to after the member name or method call.
2902 *
2903 * Returns FAIL or OK.
2904 */
2905 int
2906class_object_index(
2907 char_u **arg,
2908 typval_T *rettv,
2909 evalarg_T *evalarg,
2910 int verbose UNUSED) // give error messages
2911{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002912 if (VIM_ISWHITE((*arg)[1]))
2913 {
2914 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
2915 return FAIL;
2916 }
2917
2918 ++*arg;
2919 char_u *name = *arg;
2920 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2921 if (name_end == name)
2922 return FAIL;
2923 size_t len = name_end - name;
2924
Ernie Raeld615a312023-10-05 20:28:16 +02002925 int did_emsg_save = did_emsg;
Bram Moolenaar552bdca2023-02-17 21:08:50 +00002926 class_T *cl;
2927 if (rettv->v_type == VAR_CLASS)
2928 cl = rettv->vval.v_class;
2929 else // VAR_OBJECT
2930 {
2931 if (rettv->vval.v_object == NULL)
2932 {
2933 emsg(_(e_using_null_object));
2934 return FAIL;
2935 }
2936 cl = rettv->vval.v_object->obj_class;
2937 }
2938
Bram Moolenaard13dd302023-03-11 20:56:35 +00002939 if (cl == NULL)
2940 {
2941 emsg(_(e_incomplete_type));
2942 return FAIL;
2943 }
2944
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002945 if (*name_end == '(')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002946 // Invoke the class or object method
2947 return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002948
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002949 else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002950 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002951 // Search in the object member variable table and the class member
2952 // variable table.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002953 int is_object = rettv->v_type == VAR_OBJECT;
2954 if (get_member_tv(cl, is_object, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002955 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002956 *arg = name_end;
2957 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002958 }
2959
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002960 // could be a class method or an object method
2961 int fidx;
2962 ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
2963 if (fp != NULL)
2964 {
2965 // Private methods are not accessible outside the class
2966 if (*name == '_')
2967 {
Ernie Rael03042a22023-11-11 08:53:32 +01002968 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002969 return FAIL;
2970 }
2971
2972 partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
2973 if (pt == NULL)
2974 return FAIL;
2975
2976 pt->pt_refcount = 1;
2977 if (is_object)
2978 {
2979 pt->pt_obj = rettv->vval.v_object;
2980 ++pt->pt_obj->obj_refcount;
2981 }
2982 pt->pt_auto = TRUE;
2983 pt->pt_func = fp;
2984 func_ptr_ref(pt->pt_func);
2985 rettv->v_type = VAR_PARTIAL;
2986 rettv->vval.v_partial = pt;
2987 *arg = name_end;
2988 return OK;
2989 }
2990
Ernie Raeld615a312023-10-05 20:28:16 +02002991 if (did_emsg == did_emsg_save)
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +02002992 member_not_found_msg(cl, rettv->v_type, name, len);
Bram Moolenaard505d172022-12-18 21:42:55 +00002993 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002994
2995 return FAIL;
2996}
2997
2998/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002999 * If "arg" points to a class or object method, return it.
3000 * Otherwise return NULL.
3001 */
3002 ufunc_T *
3003find_class_func(char_u **arg)
3004{
3005 char_u *name = *arg;
3006 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
3007 if (name_end == name || *name_end != '.')
3008 return NULL;
3009
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003010 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003011 size_t len = name_end - name;
3012 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003013 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00003014 if (eval_variable(name, (int)len,
3015 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003016 return NULL;
3017 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00003018 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003019
3020 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
3021 : tv.vval.v_object->obj_class;
3022 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00003023 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003024 char_u *fname = name_end + 1;
3025 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
3026 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00003027 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003028 len = fname_end - fname;
3029
Ernie Rael4d00b832023-09-11 19:54:42 +02003030 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003031
Bram Moolenaareb533502022-12-14 15:06:11 +00003032fail_after_eval:
3033 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003034 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003035}
3036
3037/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003038 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003039 * Returns -1, if the variable is not found.
3040 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003041 */
3042 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003043class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003044{
Ernie Rael4d00b832023-09-11 19:54:42 +02003045 int idx;
3046 class_member_lookup(cl, name, namelen, &idx);
3047 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003048}
3049
3050/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003051 * Returns a pointer to the class member variable "name" in the class "cl".
3052 * Returns NULL if the variable is not found.
3053 * The member variable index is set in "idx".
3054 */
3055 ocmember_T *
3056class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3057{
Ernie Rael4d00b832023-09-11 19:54:42 +02003058 ocmember_T *ret_m = NULL;
3059 int ret_idx = -1;
3060 for (int i = 0; i < cl->class_class_member_count; ++i)
3061 {
3062 ocmember_T *m = &cl->class_class_members[i];
3063 if (namelen)
3064 {
3065 if (STRNCMP(name, m->ocm_name, namelen) == 0
3066 && m->ocm_name[namelen] == NUL)
3067 {
3068 ret_m = m;
3069 ret_idx = i;
3070 break;
3071 }
3072 }
3073 else if (STRCMP(name, m->ocm_name) == 0)
3074 {
3075 ret_m = m;
3076 ret_idx = i;
3077 break;
3078 }
3079 }
3080 if (idx != NULL)
3081 *idx = ret_idx;
3082 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003083}
3084
3085/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003086 * Returns a pointer to the class method "name" in class "cl".
3087 * Returns NULL if the method is not found.
3088 * The method index is set in "idx".
3089 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003090 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003091class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3092{
Ernie Rael4d00b832023-09-11 19:54:42 +02003093 ufunc_T *ret_fp = NULL;
3094 int ret_idx = -1;
3095 for (int i = 0; i < cl->class_class_function_count; ++i)
3096 {
3097 ufunc_T *fp = cl->class_class_functions[i];
3098 char_u *ufname = (char_u *)fp->uf_name;
3099 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
3100 {
3101 ret_fp = fp;
3102 ret_idx = i;
3103 break;
3104 }
3105 }
3106 if (idx != NULL)
3107 *idx = ret_idx;
3108 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003109}
3110
3111/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003112 * Returns the index of class method "name" in the class "cl".
3113 * Returns -1, if the method is not found.
3114 */
3115 int
3116class_method_idx(class_T *cl, char_u *name, size_t namelen)
3117{
3118 int idx;
3119 class_method_lookup(cl, name, namelen, &idx);
3120 return idx;
3121}
3122
3123/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003124 * Returns the index of object member variable "name" in the class "cl".
3125 * Returns -1, if the variable is not found.
3126 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
3127 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003128 static int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003129object_member_idx(class_T *cl, char_u *name, size_t namelen)
3130{
Ernie Rael4d00b832023-09-11 19:54:42 +02003131 int idx;
3132 object_member_lookup(cl, name, namelen, &idx);
3133 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02003134}
3135
3136/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003137 * Returns a pointer to the object member variable "name" in the class "cl".
3138 * Returns NULL if the variable is not found.
3139 * The object member variable index is set in "idx".
3140 */
3141 ocmember_T *
3142object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3143{
Ernie Rael4d00b832023-09-11 19:54:42 +02003144 ocmember_T *ret_m = NULL;
3145 int ret_idx = -1;
3146 for (int i = 0; i < cl->class_obj_member_count; ++i)
3147 {
3148 ocmember_T *m = &cl->class_obj_members[i];
3149 if (namelen)
3150 {
3151 if (STRNCMP(name, m->ocm_name, namelen) == 0
3152 && m->ocm_name[namelen] == NUL)
3153 {
3154 ret_m = m;
3155 ret_idx = i;
3156 break;
3157 }
3158 }
3159 else if (STRCMP(name, m->ocm_name) == 0)
3160 {
3161 ret_m = m;
3162 ret_idx = i;
3163 break;
3164 }
3165 }
3166 if (idx != NULL)
3167 *idx = ret_idx;
3168 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003169}
3170
3171/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003172 * Returns a pointer to the object method "name" in class "cl".
3173 * Returns NULL if the method is not found.
3174 * The object method index is set in "idx".
3175 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003176 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003177object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3178{
Ernie Rael4d00b832023-09-11 19:54:42 +02003179 ufunc_T *ret_fp = NULL;
3180 int ret_idx = -1;
3181 for (int i = 0; i < cl->class_obj_method_count; ++i)
3182 {
3183 ufunc_T *fp = cl->class_obj_methods[i];
3184 // Use a separate pointer to avoid that ASAN complains about
3185 // uf_name[] only being 4 characters.
3186 char_u *ufname = (char_u *)fp->uf_name;
3187 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
3188 {
3189 ret_fp = fp;
3190 ret_idx = i;
3191 break;
3192 }
3193 }
3194 if (idx != NULL)
3195 *idx = ret_idx;
3196 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003197}
3198
3199/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003200 * Returns the index of object method "name" in the class "cl".
3201 * Returns -1, if the method is not found.
3202 */
3203 int
3204object_method_idx(class_T *cl, char_u *name, size_t namelen)
3205{
3206 int idx;
3207 object_method_lookup(cl, name, namelen, &idx);
3208 return idx;
3209}
3210
3211/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003212 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
3213 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
3214 * object member variable.
3215 *
3216 * Returns a pointer to the member variable structure if variable is found.
3217 * Otherwise returns NULL. The member variable index is set in "*idx".
3218 */
3219 ocmember_T *
3220member_lookup(
3221 class_T *cl,
3222 vartype_T v_type,
3223 char_u *name,
3224 size_t namelen,
3225 int *idx)
3226{
3227 if (v_type == VAR_CLASS)
3228 return class_member_lookup(cl, name, namelen, idx);
3229 else
3230 return object_member_lookup(cl, name, namelen, idx);
3231}
3232
3233/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003234 * Find the class that defines the named member. Look up the hierarchy
3235 * starting at "cl".
3236 *
3237 * Return the class that defines the member "name", else NULL.
3238 * Fill in "p_m", if specified, for ocmember_T in found class.
3239 */
3240// NOTE: if useful for something could also indirectly return vartype and idx.
3241 static class_T *
3242class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m)
3243{
3244 class_T *cl_found = NULL;
3245 vartype_T vartype = VAR_UNKNOWN;
3246 ocmember_T *m_found = NULL;
3247
3248 len = len != 0 ? len : STRLEN(name);
3249
3250 // Loop assumes if member is not defined in "cl", then it is not
3251 // defined in any super class; the last class where it's found is the
3252 // class where it is defined. Once the vartype is found, the other
3253 // type is no longer checked.
3254 for (class_T *super = cl; super != NULL; super = super->class_extends)
3255 {
3256 class_T *cl_tmp = NULL;
3257 ocmember_T *m = NULL;
3258 if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT)
3259 {
3260 if ((m = object_member_lookup(super, name, len, NULL)) != NULL)
3261 {
3262 cl_tmp = super;
3263 vartype = VAR_OBJECT;
3264 }
3265 }
3266 if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS)
3267 {
3268 if (( m = class_member_lookup(super, name, len, NULL)) != NULL)
3269 {
3270 cl_tmp = super;
3271 vartype = VAR_OBJECT;
3272 }
3273 }
3274 if (cl_tmp == NULL)
3275 break; // member is not in this or any super class.
3276 cl_found = cl_tmp;
3277 m_found = m;
3278 }
3279 if (p_m != NULL)
3280 *p_m = m_found;
3281 return cl_found;
3282}
3283
3284/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003285 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
3286 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
3287 *
3288 * Returns a pointer to the method structure if variable is found.
3289 * Otherwise returns NULL. The method variable index is set in "*idx".
3290 */
3291 ufunc_T *
3292method_lookup(
3293 class_T *cl,
3294 vartype_T v_type,
3295 char_u *name,
3296 size_t namelen,
3297 int *idx)
3298{
3299 if (v_type == VAR_CLASS)
3300 return class_method_lookup(cl, name, namelen, idx);
3301 else
3302 return object_method_lookup(cl, name, namelen, idx);
3303}
3304
3305/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00003306 * Return TRUE if current context "cctx_arg" is inside class "cl".
3307 * Return FALSE if not.
3308 */
3309 int
3310inside_class(cctx_T *cctx_arg, class_T *cl)
3311{
3312 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02003313 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003314 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00003315 return TRUE;
3316 return FALSE;
3317}
3318
3319/*
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01003320 * Return TRUE if object/class variable "m" is read-only.
3321 * Also give an error message.
3322 */
3323 int
3324oc_var_check_ro(class_T *cl, ocmember_T *m)
3325{
3326 if (m->ocm_flags & (OCMFLAG_FINAL | OCMFLAG_CONST))
3327 {
3328 semsg(_(e_cannot_change_readonly_variable_str_in_class_str),
3329 m->ocm_name, cl->class_name);
3330 return TRUE;
3331 }
3332 return FALSE;
3333}
3334
3335/*
3336 * Lock all the constant object variables. Called after creating and
3337 * initializing a new object.
3338 */
3339 void
3340obj_lock_const_vars(object_T *obj)
3341{
3342 for (int i = 0; i < obj->obj_class->class_obj_member_count; i++)
3343 {
3344 ocmember_T *ocm = &obj->obj_class->class_obj_members[i];
3345 if (ocm->ocm_flags & OCMFLAG_CONST)
3346 {
3347 typval_T *mtv = ((typval_T *)(obj + 1)) + i;
3348 item_lock(mtv, DICT_MAXNEST, TRUE, TRUE);
3349 }
3350 }
3351}
3352
3353/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003354 * Make a copy of an object.
3355 */
3356 void
3357copy_object(typval_T *from, typval_T *to)
3358{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003359 if (from->vval.v_object == NULL)
3360 to->vval.v_object = NULL;
3361 else
3362 {
3363 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003364 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003365 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003366}
3367
3368/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003369 * Make a copy of a class.
3370 */
3371 void
3372copy_class(typval_T *from, typval_T *to)
3373{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003374 if (from->vval.v_class == NULL)
3375 to->vval.v_class = NULL;
3376 else
3377 {
3378 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003379 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003380 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003381}
3382
3383/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003384 * Free the class "cl" and its contents.
3385 */
3386 static void
3387class_free(class_T *cl)
3388{
3389 // Freeing what the class contains may recursively come back here.
3390 // Clear "class_name" first, if it is NULL the class does not need to
3391 // be freed.
3392 VIM_CLEAR(cl->class_name);
3393
3394 class_unref(cl->class_extends);
3395
3396 for (int i = 0; i < cl->class_interface_count; ++i)
3397 {
3398 vim_free(((char_u **)cl->class_interfaces)[i]);
3399 if (cl->class_interfaces_cl[i] != NULL)
3400 class_unref(cl->class_interfaces_cl[i]);
3401 }
3402 vim_free(cl->class_interfaces);
3403 vim_free(cl->class_interfaces_cl);
3404
3405 itf2class_T *next;
3406 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
3407 {
3408 next = i2c->i2c_next;
3409 vim_free(i2c);
3410 }
3411
3412 for (int i = 0; i < cl->class_class_member_count; ++i)
3413 {
3414 ocmember_T *m = &cl->class_class_members[i];
3415 vim_free(m->ocm_name);
3416 vim_free(m->ocm_init);
3417 if (cl->class_members_tv != NULL)
3418 clear_tv(&cl->class_members_tv[i]);
3419 }
3420 vim_free(cl->class_class_members);
3421 vim_free(cl->class_members_tv);
3422
3423 for (int i = 0; i < cl->class_obj_member_count; ++i)
3424 {
3425 ocmember_T *m = &cl->class_obj_members[i];
3426 vim_free(m->ocm_name);
3427 vim_free(m->ocm_init);
3428 }
3429 vim_free(cl->class_obj_members);
3430
3431 for (int i = 0; i < cl->class_class_function_count; ++i)
3432 {
3433 ufunc_T *uf = cl->class_class_functions[i];
3434 func_clear_free(uf, FALSE);
3435 }
3436 vim_free(cl->class_class_functions);
3437
3438 for (int i = 0; i < cl->class_obj_method_count; ++i)
3439 {
3440 ufunc_T *uf = cl->class_obj_methods[i];
3441 func_clear_free(uf, FALSE);
3442 }
3443 vim_free(cl->class_obj_methods);
3444
3445 clear_type_list(&cl->class_type_list);
3446
3447 class_cleared(cl);
3448
3449 vim_free(cl);
3450}
3451
3452/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003453 * Unreference a class. Free it when the reference count goes down to zero.
3454 */
3455 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003456class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003457{
Bram Moolenaard505d172022-12-18 21:42:55 +00003458 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003459 class_free(cl);
3460}
3461
3462/*
3463 * Go through the list of all classes and free items without "copyID".
3464 */
3465 int
3466class_free_nonref(int copyID)
3467{
3468 int did_free = FALSE;
3469
3470 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003471 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003472 next_nonref_class = cl->class_next_used;
3473 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003474 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003475 // Free the class and items it contains.
3476 class_free(cl);
3477 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003478 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003479 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003480
3481 next_nonref_class = NULL;
3482 return did_free;
3483}
3484
3485 int
3486set_ref_in_classes(int copyID)
3487{
3488 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3489 set_ref_in_item_class(cl, copyID, NULL, NULL);
3490
3491 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003492}
3493
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003494static object_T *first_object = NULL;
3495
3496/*
3497 * Call this function when an object has been created. It will be added to the
3498 * list headed by "first_object".
3499 */
3500 void
3501object_created(object_T *obj)
3502{
3503 if (first_object != NULL)
3504 {
3505 obj->obj_next_used = first_object;
3506 first_object->obj_prev_used = obj;
3507 }
3508 first_object = obj;
3509}
3510
3511/*
3512 * Call this function when an object has been cleared and is about to be freed.
3513 * It is removed from the list headed by "first_object".
3514 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003515 static void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003516object_cleared(object_T *obj)
3517{
3518 if (obj->obj_next_used != NULL)
3519 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
3520 if (obj->obj_prev_used != NULL)
3521 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
3522 else if (first_object == obj)
3523 first_object = obj->obj_next_used;
3524}
3525
3526/*
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003527 * Free the contents of an object ignoring the reference count.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003528 */
3529 static void
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003530object_free_contents(object_T *obj)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003531{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003532 class_T *cl = obj->obj_class;
3533
3534 if (!cl)
3535 return;
3536
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003537 // Avoid a recursive call, it can happen if "obj" has a circular reference.
3538 obj->obj_refcount = INT_MAX;
3539
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003540 // the member values are just after the object structure
3541 typval_T *tv = (typval_T *)(obj + 1);
3542 for (int i = 0; i < cl->class_obj_member_count; ++i)
3543 clear_tv(tv + i);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003544}
3545
3546 static void
3547object_free_object(object_T *obj)
3548{
3549 class_T *cl = obj->obj_class;
3550
3551 if (!cl)
3552 return;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003553
3554 // Remove from the list headed by "first_object".
3555 object_cleared(obj);
3556
3557 vim_free(obj);
3558 class_unref(cl);
3559}
3560
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003561 static void
3562object_free(object_T *obj)
3563{
3564 if (in_free_unref_items)
3565 return;
3566
3567 object_free_contents(obj);
3568 object_free_object(obj);
3569}
3570
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003571/*
3572 * Unreference an object.
3573 */
3574 void
3575object_unref(object_T *obj)
3576{
3577 if (obj != NULL && --obj->obj_refcount <= 0)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003578 object_free(obj);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003579}
3580
3581/*
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003582 * Go through the list of all objects and free items without "copyID".
3583 */
3584 int
3585object_free_nonref(int copyID)
3586{
3587 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003588
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003589 for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003590 {
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003591 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3592 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003593 // Free the object contents. Object itself will be freed later.
3594 object_free_contents(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003595 did_free = TRUE;
3596 }
3597 }
3598
3599 return did_free;
3600}
3601
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003602 void
3603object_free_items(int copyID)
3604{
3605 object_T *obj_next;
3606
3607 for (object_T *obj = first_object; obj != NULL; obj = obj_next)
3608 {
3609 obj_next = obj->obj_next_used;
3610 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3611 object_free_object(obj);
3612 }
3613}
3614
LemonBoyafe04662023-08-23 21:08:11 +02003615/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003616 * Output message which takes a variable name and the class that defines it.
3617 * "cl" is that class where the name was found. Search "cl"'s hierarchy to
3618 * find the defining class.
3619 */
3620 void
3621emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl)
3622{
3623 ocmember_T *m;
3624 class_T *cl_def = class_defining_member(cl, name, len, &m);
3625 if (cl_def != NULL)
3626 semsg(_(msg), m->ocm_name, cl_def->class_name);
3627 else
3628 emsg(_(e_internal_error_please_report_a_bug));
3629}
3630
3631/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003632 * Echo a class or object method not found message.
3633 */
3634 void
3635method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3636{
3637 char_u *method_name = vim_strnsave(name, len);
3638 if ((v_type == VAR_OBJECT)
3639 && (class_method_idx(cl, name, len) >= 0))
3640 {
3641 // If this is a class method, then give a different error
3642 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003643 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003644 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003645 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003646 method_name, cl->class_name);
3647 }
3648 else if ((v_type == VAR_CLASS)
3649 && (object_method_idx(cl, name, len) >= 0))
3650 {
3651 // If this is an object method, then give a different error
3652 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003653 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003654 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003655 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003656 method_name, cl->class_name);
3657 }
3658 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003659 semsg(_(e_method_not_found_on_class_str_str), method_name,
3660 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003661 vim_free(method_name);
3662}
3663
3664/*
3665 * Echo a class or object member not found message.
3666 */
3667 void
3668member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3669{
3670 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
3671
3672 if (v_type == VAR_OBJECT)
3673 {
3674 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003675 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003676 varname, cl->class_name);
3677 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003678 semsg(_(e_variable_not_found_on_object_str_str), varname,
3679 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003680 }
3681 else
3682 {
3683 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003684 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003685 varname, cl->class_name);
3686 else
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01003687 {
3688 if (IS_ENUM(cl))
3689 semsg(_(e_enum_value_str_not_found_in_enum_str),
3690 varname, cl->class_name);
3691 else
3692 semsg(_(e_class_variable_str_not_found_in_class_str),
3693 varname, cl->class_name);
3694 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003695 }
3696 vim_free(varname);
3697}
3698
3699/*
Yegappan Lakshmanan4f32c832024-01-12 17:36:40 +01003700 * Compile all the class and object methods in "cl".
3701 */
3702 void
3703defcompile_class(class_T *cl)
3704{
3705 for (int loop = 1; loop <= 2; ++loop)
3706 {
3707 int func_count = loop == 1 ? cl->class_class_function_count
3708 : cl->class_obj_method_count;
3709 for (int i = 0; i < func_count; i++)
3710 {
3711 ufunc_T *ufunc = loop == 1 ? cl->class_class_functions[i]
3712 : cl->class_obj_methods[i];
3713 defcompile_function(ufunc, cl);
3714 }
3715 }
3716}
3717
3718/*
3719 * Compile all the classes defined in the current script
3720 */
3721 void
3722defcompile_classes_in_script(void)
3723{
3724 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3725 {
3726 if (eval_variable(cl->class_name, 0, 0, NULL, NULL,
3727 EVAL_VAR_NOAUTOLOAD | EVAL_VAR_NO_FUNC) != FAIL)
3728 defcompile_class(cl);
3729 }
3730}
3731
3732/*
3733 * Returns TRUE if "name" is the name of a class. The typval for the class is
3734 * returned in "rettv".
3735 */
3736 int
3737is_class_name(char_u *name, typval_T *rettv)
3738{
3739 rettv->v_type = VAR_UNKNOWN;
3740
3741 if (eval_variable(name, 0, 0, rettv, NULL, EVAL_VAR_NOAUTOLOAD |
3742 EVAL_VAR_NO_FUNC) != FAIL)
3743 return rettv->v_type == VAR_CLASS;
3744 return FALSE;
3745}
3746
3747/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003748 * Calls the object builtin method "name" with arguments "argv". The value
3749 * returned by the builtin method is in "rettv". Returns OK or FAIL.
3750 */
3751 static int
3752object_call_builtin_method(
3753 object_T *obj,
3754 class_builtin_T builtin_method,
3755 int argc,
3756 typval_T *argv,
3757 typval_T *rettv)
3758{
3759 ufunc_T *uf;
3760 int midx;
3761
3762 if (obj == NULL)
3763 return FAIL;
3764
3765 uf = class_get_builtin_method(obj->obj_class, builtin_method, &midx);
3766 if (uf == NULL)
3767 return FAIL;
3768
3769 funccall_T *fc = create_funccal(uf, rettv);
3770 int r;
3771
3772 if (fc == NULL)
3773 return FAIL;
3774
3775 ++obj->obj_refcount;
3776
3777 r = call_def_function(uf, argc, argv, 0, NULL, obj, fc, rettv);
3778
3779 remove_funccal();
3780
3781 return r;
3782}
3783
3784/*
3785 * Calls the object "empty()" method and returns the method retun value. In
3786 * case of an error, returns TRUE.
3787 */
3788 int
3789object_empty(object_T *obj)
3790{
3791 typval_T rettv;
3792
3793 if (object_call_builtin_method(obj, CLASS_BUILTIN_EMPTY, 0, NULL, &rettv)
3794 == FAIL)
3795 return TRUE;
3796
3797 return tv_get_bool(&rettv);
3798}
3799
3800/*
3801 * Use the object "len()" method to get an object length. Returns 0 if the
3802 * method is not found or there is an error.
3803 */
3804 int
3805object_len(object_T *obj)
3806{
3807 typval_T rettv;
3808
3809 if (object_call_builtin_method(obj, CLASS_BUILTIN_LEN, 0, NULL, &rettv)
3810 == FAIL)
3811 return 0;
3812
3813 return tv_to_number(&rettv);
3814}
3815
3816/*
3817 * Return a textual representation of object "obj"
3818 */
3819 char_u *
3820object_string(
3821 object_T *obj,
3822 char_u *numbuf,
3823 int copyID,
3824 int echo_style,
3825 int restore_copyID,
3826 int composite_val)
3827{
3828 typval_T rettv;
3829
3830 if (object_call_builtin_method(obj, CLASS_BUILTIN_STRING, 0, NULL, &rettv)
3831 == OK
3832 && rettv.vval.v_string != NULL)
3833 return rettv.vval.v_string;
3834 else
3835 {
3836 garray_T ga;
3837 ga_init2(&ga, 1, 50);
3838
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003839 class_T *cl = obj == NULL ? NULL : obj->obj_class;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01003840 if (cl != NULL && IS_ENUM(cl))
3841 {
3842 ga_concat(&ga, cl->class_name);
3843 char_u *name = ((typval_T *)(obj + 1))->vval.v_string;
3844 ga_concat(&ga, (char_u *)".");
3845 ga_concat(&ga, name);
3846 return ga.ga_data;
3847 }
3848
3849 ga_concat(&ga, (char_u *)"object of ");
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003850 ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]"
3851 : cl->class_name);
3852 if (cl != NULL)
3853 {
3854 ga_concat(&ga, (char_u *)" {");
3855 for (int i = 0; i < cl->class_obj_member_count; ++i)
3856 {
3857 if (i > 0)
3858 ga_concat(&ga, (char_u *)", ");
3859 ocmember_T *m = &cl->class_obj_members[i];
3860 ga_concat(&ga, m->ocm_name);
3861 ga_concat(&ga, (char_u *)": ");
3862 char_u *tf = NULL;
3863 ga_concat(&ga, echo_string_core(
3864 (typval_T *)(obj + 1) + i,
3865 &tf, numbuf, copyID, echo_style,
3866 restore_copyID, composite_val));
3867 vim_free(tf);
3868 }
3869 ga_concat(&ga, (char_u *)"}");
3870 }
3871 return ga.ga_data;
3872 }
3873}
3874
3875/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02003876 * Return TRUE when the class "cl", its base class or one of the implemented
3877 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02003878 */
3879 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003880class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02003881{
3882 if (cl == other_cl)
3883 return TRUE;
3884
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003885 // Recursively check the base classes.
3886 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02003887 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003888 if (cl == other_cl)
3889 return TRUE;
3890 // Check the implemented interfaces and the super interfaces
3891 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003892 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003893 class_T *intf = cl->class_interfaces_cl[i];
3894 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003895 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003896 if (intf == other_cl)
3897 return TRUE;
3898 // check the super interfaces
3899 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003900 }
3901 }
LemonBoyafe04662023-08-23 21:08:11 +02003902 }
3903
3904 return FALSE;
3905}
3906
3907/*
Ernie Rael2025af12023-12-12 16:58:00 +01003908 * "instanceof(object, classinfo, ...)" function
LemonBoyafe04662023-08-23 21:08:11 +02003909 */
3910 void
3911f_instanceof(typval_T *argvars, typval_T *rettv)
3912{
3913 typval_T *object_tv = &argvars[0];
3914 typval_T *classinfo_tv = &argvars[1];
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02003915 class_T *c;
LemonBoyafe04662023-08-23 21:08:11 +02003916
3917 rettv->vval.v_number = VVAL_FALSE;
3918
3919 if (check_for_object_arg(argvars, 0) == FAIL
Ernie Rael2025af12023-12-12 16:58:00 +01003920 || check_for_class_or_typealias_args(argvars, 1) == FAIL)
LemonBoyafe04662023-08-23 21:08:11 +02003921 return;
3922
Ernie Rael3da696d2023-09-19 20:14:18 +02003923 if (object_tv->vval.v_object == NULL)
3924 return;
3925
Ernie Rael2025af12023-12-12 16:58:00 +01003926 for (; classinfo_tv->v_type != VAR_UNKNOWN; ++classinfo_tv)
LemonBoyafe04662023-08-23 21:08:11 +02003927 {
Ernie Rael2025af12023-12-12 16:58:00 +01003928 if (classinfo_tv->v_type == VAR_TYPEALIAS)
3929 c = classinfo_tv->vval.v_typealias->ta_type->tt_class;
3930 else
3931 c = classinfo_tv->vval.v_class;
3932
3933 if (class_instance_of(object_tv->vval.v_object->obj_class, c))
LemonBoyafe04662023-08-23 21:08:11 +02003934 {
Ernie Rael2025af12023-12-12 16:58:00 +01003935 rettv->vval.v_number = VVAL_TRUE;
3936 return;
LemonBoyafe04662023-08-23 21:08:11 +02003937 }
3938 }
LemonBoyafe04662023-08-23 21:08:11 +02003939}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00003940
3941#endif // FEAT_EVAL