blob: 52c2f764db819ec4b7481985d4507529976ac3eb [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;
Yegappan Lakshmananabedca92024-03-29 10:08:23 +01002059
2060 if (enum_end)
2061 // Add the enum "values" class variable.
2062 enum_add_values_member(cl, &classmembers, num_enum_values,
2063 &type_list);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002064 continue;
2065 }
2066
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002067 int has_public = FALSE;
2068 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002069 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002070 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002071 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002072 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002073 break;
2074 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002075 if (is_interface)
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002076 {
Yegappan Lakshmananb90e3bc2023-09-28 23:06:48 +02002077 emsg(_(e_public_variable_not_supported_in_interface));
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002078 break;
2079 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002080 has_public = TRUE;
2081 p = skipwhite(line + 6);
2082
Yegappan Lakshmananc51578f2024-04-13 17:58:09 +02002083 if (STRNCMP(p, "def", 3) == 0)
2084 {
2085 emsg(_(e_public_keyword_not_supported_for_method));
2086 break;
2087 }
2088
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002089 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "static", 6) != 0
2090 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002091 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002092 emsg(_(e_public_must_be_followed_by_var_static_final_or_const));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002093 break;
2094 }
2095 }
Bram Moolenaard505d172022-12-18 21:42:55 +00002096
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002097 int abstract_method = FALSE;
2098 char_u *pa = p;
2099 if (checkforcmd(&p, "abstract", 3))
2100 {
2101 if (STRNCMP(pa, "abstract", 8) != 0)
2102 {
2103 semsg(_(e_command_cannot_be_shortened_str), pa);
2104 break;
2105 }
2106
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002107 if (is_enum)
2108 {
2109 // "abstract" not supported in an enum
2110 emsg(_(e_abstract_cannot_be_used_in_enum));
2111 break;
2112 }
2113
2114 if (is_interface)
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01002115 {
2116 // "abstract" not supported in an interface
2117 emsg(_(e_abstract_cannot_be_used_in_interface));
2118 break;
2119 }
2120
2121 if (!is_abstract)
2122 {
2123 semsg(_(e_abstract_method_in_concrete_class), pa);
2124 break;
2125 }
2126
Yegappan Lakshmanan5a539252023-11-04 09:42:46 +01002127 p = skipwhite(pa + 8);
2128 if (STRNCMP(p, "def", 3) != 0)
2129 {
2130 emsg(_(e_abstract_must_be_followed_by_def));
2131 break;
2132 }
2133
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01002134 abstract_method = TRUE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002135 }
2136
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002137 int has_static = FALSE;
2138 char_u *ps = p;
2139 if (checkforcmd(&p, "static", 4))
2140 {
2141 if (STRNCMP(ps, "static", 6) != 0)
2142 {
2143 semsg(_(e_command_cannot_be_shortened_str), ps);
2144 break;
2145 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002146
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002147 if (is_interface)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002148 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002149 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002150 break;
2151 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002152 has_static = TRUE;
2153 p = skipwhite(ps + 6);
Doug Kearns74da0ee2023-12-14 20:26:26 +01002154
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002155 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "def", 3) != 0
2156 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Doug Kearns74da0ee2023-12-14 20:26:26 +01002157 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002158 emsg(_(e_static_must_be_followed_by_var_def_final_or_const));
Doug Kearns74da0ee2023-12-14 20:26:26 +01002159 break;
2160 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002161 }
2162
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002163 int has_final = FALSE;
2164 int has_var = FALSE;
2165 int has_const = FALSE;
2166 if (checkforcmd(&p, "var", 3))
2167 has_var = TRUE;
2168 else if (checkforcmd(&p, "final", 5))
2169 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002170 if (is_interface)
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002171 {
2172 emsg(_(e_final_variable_not_supported_in_interface));
2173 break;
2174 }
2175 has_final = TRUE;
2176 }
2177 else if (checkforcmd(&p, "const", 5))
2178 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002179 if (is_interface)
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002180 {
2181 emsg(_(e_const_variable_not_supported_in_interface));
2182 break;
2183 }
2184 has_const = TRUE;
2185 }
2186 p = skipwhite(p);
2187
Bram Moolenaard505d172022-12-18 21:42:55 +00002188 // object members (public, read access, private):
Doug Kearns74da0ee2023-12-14 20:26:26 +01002189 // "var _varname"
2190 // "var varname"
2191 // "public var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002192 // "final _varname"
2193 // "final varname"
2194 // "public final varname"
2195 // "const _varname"
2196 // "const varname"
2197 // "public const varname"
Doug Kearns74da0ee2023-12-14 20:26:26 +01002198 // class members (public, read access, private):
2199 // "static var _varname"
2200 // "static var varname"
2201 // "public static var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002202 // "static final _varname"
2203 // "static final varname"
2204 // "public static final varname"
2205 // "static const _varname"
2206 // "static const varname"
2207 // "public static const varname"
2208 if (has_var || has_final || has_const)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00002209 {
Doug Kearns74da0ee2023-12-14 20:26:26 +01002210 char_u *varname = p;
Bram Moolenaard505d172022-12-18 21:42:55 +00002211 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00002212 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00002213 char_u *init_expr = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002214 int has_type = FALSE;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002215
Doug Kearns74da0ee2023-12-14 20:26:26 +01002216 if (!eval_isnamec1(*p))
2217 {
2218 if (has_static)
2219 semsg(_(e_invalid_class_variable_declaration_str), line);
2220 else
2221 semsg(_(e_invalid_object_variable_declaration_str), line);
2222 break;
2223 }
2224
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002225 if (is_interface && *varname == '_')
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002226 {
2227 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01002228 semsg(_(e_protected_variable_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002229 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002230 break;
2231 }
2232
Bram Moolenaard505d172022-12-18 21:42:55 +00002233 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002234 &varname_end, &has_type, &type_list, &type,
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002235 !is_interface ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002236 break;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002237
2238 if (is_reserved_varname(varname, varname_end)
2239 || is_duplicate_variable(&classmembers, &objmembers,
2240 varname, varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02002241 {
2242 vim_free(init_expr);
2243 break;
2244 }
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002245 if (add_member(has_static ? &classmembers : &objmembers, varname,
2246 varname_end, has_public, has_final, has_const,
2247 has_type, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00002248 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002249 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002250 break;
2251 }
Bram Moolenaard505d172022-12-18 21:42:55 +00002252 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002253
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002254 // constructors:
2255 // def new()
2256 // enddef
2257 // def newOther()
2258 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002259 // object methods and class functions:
2260 // def SomeMethod()
2261 // enddef
2262 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002263 // enddef
2264 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002265 // def <Tval> someMethod()
2266 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002267 else if (checkforcmd(&p, "def", 3))
2268 {
2269 exarg_T ea;
2270 garray_T lines_to_free;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002271 int is_new = STRNCMP(p, "new", 3) == 0;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002272
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02002273 if (has_public)
2274 {
2275 // "public" keyword is not supported when defining an object or
2276 // class method
2277 emsg(_(e_public_keyword_not_supported_for_method));
2278 break;
2279 }
2280
2281 if (*p == NUL)
2282 {
2283 // No method name following def
2284 semsg(_(e_not_valid_command_in_class_str), line);
2285 break;
2286 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002287
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002288 if (is_interface && *p == '_')
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01002289 {
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002290 // private methods are not supported in an interface
2291 semsg(_(e_protected_method_not_supported_in_interface), p);
2292 break;
2293 }
2294
2295 if (has_static && !is_new && SAFE_islower(*p) &&
2296 is_valid_builtin_obj_methodname(p))
2297 {
2298 semsg(_(e_builtin_class_method_not_supported), p);
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01002299 break;
2300 }
2301
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002302 CLEAR_FIELD(ea);
2303 ea.cmd = line;
2304 ea.arg = p;
2305 ea.cmdidx = CMD_def;
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002306 ea.ea_getline = eap->ea_getline;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002307 ea.cookie = eap->cookie;
2308
2309 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002310 int class_flags;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002311 if (is_interface)
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002312 class_flags = CF_INTERFACE;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002313 else
2314 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
Bram Moolenaar554d0312023-01-05 19:59:18 +00002315 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02002316 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002317 ga_clear_strings(&lines_to_free);
2318
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002319 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002320 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002321 char_u *name = uf->uf_name;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02002322
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002323 if (is_new && !is_valid_constructor(uf, is_abstract,
2324 has_static))
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002325 {
2326 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01002327 semsg(_(e_protected_method_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002328 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002329 func_clear_free(uf, FALSE);
2330 break;
2331 }
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002332
2333 // check for builtin method
2334 if (!is_new && SAFE_islower(*name) &&
2335 !object_check_builtin_method_sig(uf))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00002336 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02002337 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00002338 break;
2339 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02002340
Bram Moolenaar58b40092023-01-11 15:59:05 +00002341 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002342 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002343 {
2344 success = FALSE;
2345 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02002346 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002347 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00002348
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002349 garray_T *fgap = has_static || is_new
2350 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002351 if (ga_grow(fgap, 1) == OK)
2352 {
2353 if (is_new)
2354 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002355
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002356 if (abstract_method)
2357 uf->uf_flags |= FC_ABSTRACT;
2358
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002359 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
2360 ++fgap->ga_len;
2361 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002362 }
2363 }
2364
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002365 else
2366 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00002367 if (is_class)
2368 semsg(_(e_not_valid_command_in_class_str), line);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002369 else if (is_enum)
2370 semsg(_(e_not_valid_command_in_enum_str), line);
Bram Moolenaar554d0312023-01-05 19:59:18 +00002371 else
2372 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002373 break;
2374 }
2375 }
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002376
2377 if (theline == NULL && !success && is_enum)
2378 emsg(_(e_missing_endenum));
2379
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002380 vim_free(theline);
2381
Yegappan Lakshmananabedca92024-03-29 10:08:23 +01002382 if (success && is_enum && num_enum_values == 0)
2383 // Empty enum statement. Add an empty "values" class variable
2384 enum_add_values_member(cl, &classmembers, 0, &type_list);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002385
Bram Moolenaar83677162023-01-08 19:54:10 +00002386 /*
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002387 * Check a few things
Bram Moolenaar83677162023-01-08 19:54:10 +00002388 */
2389
2390 // Check the "extends" class is valid.
2391 if (success && extends != NULL)
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002392 success = validate_extends_class(cl, extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00002393 VIM_CLEAR(extends);
2394
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002395 // Check the new object methods to make sure their access (public or
2396 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002397 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002398 success = validate_extends_methods(&objmethods, extends_cl);
2399
2400 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002401 // variables in the extended class lineage. If an interface is extending
2402 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002403 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002404 {
2405 if (is_class)
2406 success = extends_check_dup_members(&objmembers, extends_cl);
2407 else
2408 success = extends_check_intf_var_type(&objmembers, extends_cl);
2409 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002410
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002411 // When extending an abstract class, make sure all the abstract methods in
2412 // the parent class are implemented. If the current class is an abstract
2413 // class, then there is no need for this check.
2414 if (success && !is_abstract && extends_cl != NULL
2415 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002416 success = validate_abstract_class_methods(&classfunctions,
2417 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002418
Bram Moolenaar83677162023-01-08 19:54:10 +00002419 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00002420 if (success && ga_impl.ga_len > 0)
2421 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002422 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
2423
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002424 success = validate_implements_classes(&ga_impl, intf_classes,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002425 &objmethods, &objmembers, extends_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00002426 }
2427
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002428 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00002429 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002430 success = check_func_arg_names(&classfunctions, &objmethods,
2431 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00002432
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002433 if (success)
2434 {
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002435 // "endclass" or "endinterface" or "endenum" encountered without any
2436 // failures
Bram Moolenaard505d172022-12-18 21:42:55 +00002437
Bram Moolenaard0200c82023-01-28 15:19:40 +00002438 if (extends_cl != NULL)
2439 {
2440 cl->class_extends = extends_cl;
2441 extends_cl->class_flags |= CLASS_EXTENDED;
2442 }
Bram Moolenaar83677162023-01-08 19:54:10 +00002443
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002444 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00002445 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002446 NULL,
2447 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00002448 &cl->class_class_members,
2449 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00002450 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00002451 extends_cl == NULL ? NULL
2452 : extends_cl->class_obj_members,
2453 extends_cl == NULL ? 0
2454 : extends_cl->class_obj_member_count,
2455 &cl->class_obj_members,
2456 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002457 goto cleanup;
2458
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00002459 if (ga_impl.ga_len > 0)
2460 {
2461 // Move the "implements" names into the class.
2462 cl->class_interface_count = ga_impl.ga_len;
2463 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
2464 if (cl->class_interfaces == NULL)
2465 goto cleanup;
2466 for (int i = 0; i < ga_impl.ga_len; ++i)
2467 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
2468 VIM_CLEAR(ga_impl.ga_data);
2469 ga_impl.ga_len = 0;
2470
Bram Moolenaard0200c82023-01-28 15:19:40 +00002471 cl->class_interfaces_cl = intf_classes;
2472 intf_classes = NULL;
2473 }
2474
2475 if (cl->class_interface_count > 0 || extends_cl != NULL)
2476 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002477 // Add a method and member lookup table to each of the interface
2478 // classes.
2479 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
2480 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00002481 }
2482
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002483 int have_new = FALSE;
2484 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002485 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002486 {
2487 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
2488 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002489 {
2490 have_new = TRUE;
2491 break;
2492 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002493 }
2494
2495 if (have_new)
2496 // The return type of new() is an object of class "cl"
2497 class_func->uf_ret_type->tt_class = cl;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002498 else if ((is_class || is_enum) && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002499 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002500 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002501
Bram Moolenaar58b40092023-01-11 15:59:05 +00002502 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002503 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
2504 &objmethods) == FAIL)
2505 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002506
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002507 update_builtin_method_index(cl);
2508
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002509 class_created(cl);
2510
2511 // Allocate a typval for each class member and initialize it.
2512 if ((is_class || is_enum) && cl->class_class_member_count > 0)
2513 if (add_class_members(cl, eap, &type_list) == FAIL)
2514 goto cleanup;
2515
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002516 cl->class_type_list = type_list;
2517
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01002518 if (is_enum)
2519 {
2520 // clear the constructor method names, so that an enum class cannot
2521 // be instantiated
2522 enum_clear_constructors(cl);
2523 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002524
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002525 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00002526 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002527
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002528 return;
2529 }
2530
2531cleanup:
Bram Moolenaar83677162023-01-08 19:54:10 +00002532 vim_free(extends);
2533 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002534
2535 if (intf_classes != NULL)
2536 {
2537 for (int i = 0; i < ga_impl.ga_len; ++i)
2538 class_unref(intf_classes[i]);
2539 vim_free(intf_classes);
2540 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00002541 ga_clear_strings(&ga_impl);
2542
Bram Moolenaard505d172022-12-18 21:42:55 +00002543 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002544 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002545 garray_T *gap = round == 1 ? &classmembers : &objmembers;
2546 if (gap->ga_len == 0 || gap->ga_data == NULL)
2547 continue;
2548
2549 for (int i = 0; i < gap->ga_len; ++i)
2550 {
2551 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2552 vim_free(m->ocm_name);
2553 vim_free(m->ocm_init);
2554 }
2555 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002556 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002557
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002558 for (int i = 0; i < objmethods.ga_len; ++i)
2559 {
2560 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2561 func_clear_free(uf, FALSE);
2562 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002563 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002564
2565 for (int i = 0; i < classfunctions.ga_len; ++i)
2566 {
2567 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2568 func_clear_free(uf, FALSE);
2569 }
2570 ga_clear(&classfunctions);
2571
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002572 clear_type_list(&type_list);
2573}
2574
2575/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002576 * Find member "name" in class "cl", set "member_idx" to the member index and
2577 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002578 * When "is_object" is TRUE, then look for object members. Otherwise look for
2579 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002580 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002581 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002582 */
2583 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002584oc_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002585 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002586 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002587 char_u *name,
2588 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002589 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002590{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002591 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002592 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002593
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002594 *member_idx = -1; // not found (yet)
2595
2596 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2597 member_idx);
2598 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002599 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002600 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2601 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002602 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002603 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002604
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002605 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002606}
2607
2608/*
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002609 * Given a class or object variable index, return the variable type
2610 */
2611 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002612oc_member_type_by_idx(
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002613 class_T *cl,
2614 int is_object,
2615 int member_idx)
2616{
2617 ocmember_T *m;
2618 int member_count;
2619
2620 if (is_object)
2621 {
2622 m = cl->class_obj_members;
2623 member_count = cl->class_obj_member_count;
2624 }
2625 else
2626 {
2627 m = cl->class_class_members;
2628 member_count = cl->class_class_member_count;
2629 }
2630
2631 if (member_idx >= member_count)
2632 return NULL;
2633
2634 return m[member_idx].ocm_type;
2635}
2636
2637/*
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002638 * Type aliases (:type)
2639 */
2640
2641 void
2642typealias_free(typealias_T *ta)
2643{
2644 // ta->ta_type is freed in clear_type_list()
2645 vim_free(ta->ta_name);
2646 vim_free(ta);
2647}
2648
2649 void
2650typealias_unref(typealias_T *ta)
2651{
2652 if (ta != NULL && --ta->ta_refcount <= 0)
2653 typealias_free(ta);
2654}
2655
2656/*
2657 * Handle ":type". Create an alias for a type specification.
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002658 */
2659 void
2660ex_type(exarg_T *eap UNUSED)
2661{
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002662 char_u *arg = eap->arg;
2663
2664 if (!current_script_is_vim9()
2665 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002666 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002667 {
2668 emsg(_(e_type_can_only_be_defined_in_vim9_script));
2669 return;
2670 }
2671
2672 if (*arg == NUL)
2673 {
2674 emsg(_(e_missing_typealias_name));
2675 return;
2676 }
2677
2678 if (!ASCII_ISUPPER(*arg))
2679 {
2680 semsg(_(e_type_name_must_start_with_uppercase_letter_str), arg);
2681 return;
2682 }
2683
2684 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
2685 if (!IS_WHITE_OR_NUL(*name_end))
2686 {
2687 semsg(_(e_white_space_required_after_name_str), arg);
2688 return;
2689 }
2690 char_u *name_start = arg;
2691
2692 arg = skipwhite(name_end);
2693 if (*arg != '=')
2694 {
2695 semsg(_(e_missing_equal_str), arg);
2696 return;
2697 }
2698 if (!IS_WHITE_OR_NUL(*(arg + 1)))
2699 {
2700 semsg(_(e_white_space_required_after_str_str), "=", arg);
2701 return;
2702 }
2703 arg++;
2704 arg = skipwhite(arg);
2705
2706 if (*arg == NUL)
2707 {
2708 emsg(_(e_missing_typealias_type));
2709 return;
2710 }
2711
2712 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
2713 type_T *type = parse_type(&arg, &si->sn_type_list, TRUE);
2714 if (type == NULL)
2715 return;
2716
2717 if (*arg != NUL)
2718 {
2719 // some text after the type
2720 semsg(_(e_trailing_characters_str), arg);
2721 return;
2722 }
2723
2724 int cc = *name_end;
2725 *name_end = NUL;
2726
2727 typval_T tv;
2728 tv.v_type = VAR_UNKNOWN;
2729 if (eval_variable_import(name_start, &tv) == OK)
2730 {
2731 if (tv.v_type == VAR_TYPEALIAS)
2732 semsg(_(e_typealias_already_exists_for_str), name_start);
2733 else
2734 semsg(_(e_redefining_script_item_str), name_start);
2735 clear_tv(&tv);
2736 goto done;
2737 }
2738
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02002739 // Create a script-local variable for the type alias.
2740 if (type->tt_type != VAR_OBJECT)
2741 {
2742 tv.v_type = VAR_TYPEALIAS;
2743 tv.v_lock = 0;
2744 tv.vval.v_typealias = ALLOC_CLEAR_ONE(typealias_T);
2745 ++tv.vval.v_typealias->ta_refcount;
2746 tv.vval.v_typealias->ta_name = vim_strsave(name_start);
2747 tv.vval.v_typealias->ta_type = type;
2748 }
2749 else
2750 {
2751 // When creating a type alias for a class, use the class type itself to
2752 // create the type alias variable. This is needed to use the type
2753 // alias to invoke class methods (e.g. new()) and use class variables.
2754 tv.v_type = VAR_CLASS;
2755 tv.v_lock = 0;
2756 tv.vval.v_class = type->tt_class;
2757 ++tv.vval.v_class->class_refcount;
2758 }
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002759 set_var_const(name_start, current_sctx.sc_sid, NULL, &tv, FALSE,
2760 ASSIGN_CONST | ASSIGN_FINAL, 0);
2761
2762done:
2763 *name_end = cc;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002764}
2765
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002766/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002767 * Returns OK if a member variable named "name" is present in the class "cl".
2768 * Otherwise returns FAIL. If found, the member variable typval is set in
2769 * "rettv". If "is_object" is TRUE, then the object member variable table is
2770 * searched. Otherwise the class member variable table is searched.
2771 */
2772 static int
2773get_member_tv(
2774 class_T *cl,
2775 int is_object,
2776 char_u *name,
2777 size_t namelen,
2778 typval_T *rettv)
2779{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002780 ocmember_T *m;
2781 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002782
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002783 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2784 &m_idx);
2785 if (m == NULL)
2786 return FAIL;
2787
2788 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002789 {
Ernie Rael03042a22023-11-11 08:53:32 +01002790 emsg_var_cl_define(e_cannot_access_protected_variable_str,
Ernie Raele6c9aa52023-10-06 19:55:52 +02002791 m->ocm_name, 0, cl);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002792 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002793 }
2794
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002795 if (is_object)
2796 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002797 // The object only contains a pointer to the class, the member values
2798 // array follows right after that.
2799 object_T *obj = rettv->vval.v_object;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002800 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2801 copy_tv(tv, rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002802 object_unref(obj);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002803 }
2804 else
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002805 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002806 copy_tv(&cl->class_members_tv[m_idx], rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002807 class_unref(cl);
2808 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002809
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002810 return OK;
2811}
2812
2813/*
2814 * Call an object or class method "name" in class "cl". The method return
2815 * value is returned in "rettv".
2816 */
2817 static int
2818call_oc_method(
2819 class_T *cl,
2820 char_u *name,
2821 size_t len,
2822 char_u *name_end,
2823 evalarg_T *evalarg,
2824 char_u **arg,
2825 typval_T *rettv)
2826{
2827 ufunc_T *fp;
2828 typval_T argvars[MAX_FUNC_ARGS + 1];
2829 int argcount = 0;
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002830 ocmember_T *ocm = NULL;
2831 int m_idx;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002832
2833 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
2834 if (fp == NULL)
2835 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002836 // could be an object or class funcref variable
2837 ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
2838 if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
2839 {
2840 method_not_found_msg(cl, rettv->v_type, name, len);
2841 return FAIL;
2842 }
2843
Yegappan Lakshmanan3e336502024-04-04 19:35:59 +02002844 if (*name == '_')
2845 {
2846 // Protected object or class funcref variable
2847 semsg(_(e_cannot_access_protected_variable_str), ocm->ocm_name,
2848 cl->class_name);
2849 return FAIL;
2850 }
2851
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002852 if (rettv->v_type == VAR_OBJECT)
2853 {
2854 // funcref object variable
2855 object_T *obj = rettv->vval.v_object;
2856 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2857 copy_tv(tv, rettv);
2858 }
2859 else
2860 // funcref class variable
2861 copy_tv(&cl->class_members_tv[m_idx], rettv);
2862 *arg = name_end;
2863 return OK;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002864 }
2865
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002866 if (ocm == NULL && *fp->uf_name == '_')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002867 {
2868 // Cannot access a private method outside of a class
Ernie Rael03042a22023-11-11 08:53:32 +01002869 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002870 return FAIL;
2871 }
2872
2873 char_u *argp = name_end;
Ernie Raelb077b582023-12-14 20:11:44 +01002874 int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount, FALSE);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002875 if (ret == FAIL)
2876 return FAIL;
2877
2878 funcexe_T funcexe;
2879 CLEAR_FIELD(funcexe);
2880 funcexe.fe_evaluate = TRUE;
2881 if (rettv->v_type == VAR_OBJECT)
2882 {
2883 funcexe.fe_object = rettv->vval.v_object;
2884 ++funcexe.fe_object->obj_refcount;
2885 }
2886
2887 // Clear the class or object after calling the function, in
2888 // case the refcount is one.
2889 typval_T tv_tofree = *rettv;
2890 rettv->v_type = VAR_UNKNOWN;
2891
2892 // Call the user function. Result goes into rettv;
2893 int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
2894 NULL);
2895
2896 // Clear the previous rettv and the arguments.
2897 clear_tv(&tv_tofree);
2898 for (int idx = 0; idx < argcount; ++idx)
2899 clear_tv(&argvars[idx]);
2900
2901 if (error != FCERR_NONE)
2902 {
2903 user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
2904 return FAIL;
2905 }
2906 *arg = argp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002907
2908 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002909}
2910
2911/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002912 * Evaluate what comes after a class:
2913 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002914 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002915 * - class constructor: SomeClass.new()
2916 * - object member: someObject.varname
2917 * - object method: someObject.SomeMethod()
2918 *
2919 * "*arg" points to the '.'.
2920 * "*arg" is advanced to after the member name or method call.
2921 *
2922 * Returns FAIL or OK.
2923 */
2924 int
2925class_object_index(
2926 char_u **arg,
2927 typval_T *rettv,
2928 evalarg_T *evalarg,
2929 int verbose UNUSED) // give error messages
2930{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002931 if (VIM_ISWHITE((*arg)[1]))
2932 {
2933 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
2934 return FAIL;
2935 }
2936
2937 ++*arg;
2938 char_u *name = *arg;
2939 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2940 if (name_end == name)
2941 return FAIL;
2942 size_t len = name_end - name;
2943
Ernie Raeld615a312023-10-05 20:28:16 +02002944 int did_emsg_save = did_emsg;
Bram Moolenaar552bdca2023-02-17 21:08:50 +00002945 class_T *cl;
2946 if (rettv->v_type == VAR_CLASS)
2947 cl = rettv->vval.v_class;
2948 else // VAR_OBJECT
2949 {
2950 if (rettv->vval.v_object == NULL)
2951 {
2952 emsg(_(e_using_null_object));
2953 return FAIL;
2954 }
2955 cl = rettv->vval.v_object->obj_class;
2956 }
2957
Bram Moolenaard13dd302023-03-11 20:56:35 +00002958 if (cl == NULL)
2959 {
2960 emsg(_(e_incomplete_type));
2961 return FAIL;
2962 }
2963
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002964 if (*name_end == '(')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002965 // Invoke the class or object method
2966 return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002967
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002968 else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002969 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002970 // Search in the object member variable table and the class member
2971 // variable table.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002972 int is_object = rettv->v_type == VAR_OBJECT;
2973 if (get_member_tv(cl, is_object, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002974 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002975 *arg = name_end;
2976 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002977 }
2978
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002979 // could be a class method or an object method
2980 int fidx;
2981 ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
2982 if (fp != NULL)
2983 {
2984 // Private methods are not accessible outside the class
2985 if (*name == '_')
2986 {
Ernie Rael03042a22023-11-11 08:53:32 +01002987 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002988 return FAIL;
2989 }
2990
2991 partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
2992 if (pt == NULL)
2993 return FAIL;
2994
2995 pt->pt_refcount = 1;
2996 if (is_object)
2997 {
2998 pt->pt_obj = rettv->vval.v_object;
2999 ++pt->pt_obj->obj_refcount;
3000 }
3001 pt->pt_auto = TRUE;
3002 pt->pt_func = fp;
3003 func_ptr_ref(pt->pt_func);
3004 rettv->v_type = VAR_PARTIAL;
3005 rettv->vval.v_partial = pt;
3006 *arg = name_end;
3007 return OK;
3008 }
3009
Ernie Raeld615a312023-10-05 20:28:16 +02003010 if (did_emsg == did_emsg_save)
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +02003011 member_not_found_msg(cl, rettv->v_type, name, len);
Bram Moolenaard505d172022-12-18 21:42:55 +00003012 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003013
3014 return FAIL;
3015}
3016
3017/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003018 * If "arg" points to a class or object method, return it.
3019 * Otherwise return NULL.
3020 */
3021 ufunc_T *
3022find_class_func(char_u **arg)
3023{
3024 char_u *name = *arg;
3025 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
3026 if (name_end == name || *name_end != '.')
3027 return NULL;
3028
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003029 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02003030 size_t len = name_end - name;
3031 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003032 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00003033 if (eval_variable(name, (int)len,
3034 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003035 return NULL;
3036 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00003037 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003038
3039 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
3040 : tv.vval.v_object->obj_class;
3041 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00003042 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003043 char_u *fname = name_end + 1;
3044 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
3045 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00003046 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003047 len = fname_end - fname;
3048
Ernie Rael4d00b832023-09-11 19:54:42 +02003049 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003050
Bram Moolenaareb533502022-12-14 15:06:11 +00003051fail_after_eval:
3052 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003053 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00003054}
3055
3056/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003057 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003058 * Returns -1, if the variable is not found.
3059 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003060 */
3061 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003062class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003063{
Ernie Rael4d00b832023-09-11 19:54:42 +02003064 int idx;
3065 class_member_lookup(cl, name, namelen, &idx);
3066 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00003067}
3068
3069/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003070 * Returns a pointer to the class member variable "name" in the class "cl".
3071 * Returns NULL if the variable is not found.
3072 * The member variable index is set in "idx".
3073 */
3074 ocmember_T *
3075class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3076{
Ernie Rael4d00b832023-09-11 19:54:42 +02003077 ocmember_T *ret_m = NULL;
3078 int ret_idx = -1;
3079 for (int i = 0; i < cl->class_class_member_count; ++i)
3080 {
3081 ocmember_T *m = &cl->class_class_members[i];
3082 if (namelen)
3083 {
3084 if (STRNCMP(name, m->ocm_name, namelen) == 0
3085 && m->ocm_name[namelen] == NUL)
3086 {
3087 ret_m = m;
3088 ret_idx = i;
3089 break;
3090 }
3091 }
3092 else if (STRCMP(name, m->ocm_name) == 0)
3093 {
3094 ret_m = m;
3095 ret_idx = i;
3096 break;
3097 }
3098 }
3099 if (idx != NULL)
3100 *idx = ret_idx;
3101 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003102}
3103
3104/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003105 * Returns a pointer to the class method "name" in class "cl".
3106 * Returns NULL if the method is not found.
3107 * The method index is set in "idx".
3108 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003109 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003110class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3111{
Ernie Rael4d00b832023-09-11 19:54:42 +02003112 ufunc_T *ret_fp = NULL;
3113 int ret_idx = -1;
3114 for (int i = 0; i < cl->class_class_function_count; ++i)
3115 {
3116 ufunc_T *fp = cl->class_class_functions[i];
3117 char_u *ufname = (char_u *)fp->uf_name;
3118 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
3119 {
3120 ret_fp = fp;
3121 ret_idx = i;
3122 break;
3123 }
3124 }
3125 if (idx != NULL)
3126 *idx = ret_idx;
3127 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003128}
3129
3130/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003131 * Returns the index of class method "name" in the class "cl".
3132 * Returns -1, if the method is not found.
3133 */
3134 int
3135class_method_idx(class_T *cl, char_u *name, size_t namelen)
3136{
3137 int idx;
3138 class_method_lookup(cl, name, namelen, &idx);
3139 return idx;
3140}
3141
3142/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003143 * Returns the index of object member variable "name" in the class "cl".
3144 * Returns -1, if the variable is not found.
3145 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
3146 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003147 static int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003148object_member_idx(class_T *cl, char_u *name, size_t namelen)
3149{
Ernie Rael4d00b832023-09-11 19:54:42 +02003150 int idx;
3151 object_member_lookup(cl, name, namelen, &idx);
3152 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02003153}
3154
3155/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003156 * Returns a pointer to the object member variable "name" in the class "cl".
3157 * Returns NULL if the variable is not found.
3158 * The object member variable index is set in "idx".
3159 */
3160 ocmember_T *
3161object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3162{
Ernie Rael4d00b832023-09-11 19:54:42 +02003163 ocmember_T *ret_m = NULL;
3164 int ret_idx = -1;
3165 for (int i = 0; i < cl->class_obj_member_count; ++i)
3166 {
3167 ocmember_T *m = &cl->class_obj_members[i];
3168 if (namelen)
3169 {
3170 if (STRNCMP(name, m->ocm_name, namelen) == 0
3171 && m->ocm_name[namelen] == NUL)
3172 {
3173 ret_m = m;
3174 ret_idx = i;
3175 break;
3176 }
3177 }
3178 else if (STRCMP(name, m->ocm_name) == 0)
3179 {
3180 ret_m = m;
3181 ret_idx = i;
3182 break;
3183 }
3184 }
3185 if (idx != NULL)
3186 *idx = ret_idx;
3187 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003188}
3189
3190/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003191 * Returns a pointer to the object method "name" in class "cl".
3192 * Returns NULL if the method is not found.
3193 * The object method index is set in "idx".
3194 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003195 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003196object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
3197{
Ernie Rael4d00b832023-09-11 19:54:42 +02003198 ufunc_T *ret_fp = NULL;
3199 int ret_idx = -1;
3200 for (int i = 0; i < cl->class_obj_method_count; ++i)
3201 {
3202 ufunc_T *fp = cl->class_obj_methods[i];
3203 // Use a separate pointer to avoid that ASAN complains about
3204 // uf_name[] only being 4 characters.
3205 char_u *ufname = (char_u *)fp->uf_name;
3206 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
3207 {
3208 ret_fp = fp;
3209 ret_idx = i;
3210 break;
3211 }
3212 }
3213 if (idx != NULL)
3214 *idx = ret_idx;
3215 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003216}
3217
3218/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003219 * Returns the index of object method "name" in the class "cl".
3220 * Returns -1, if the method is not found.
3221 */
3222 int
3223object_method_idx(class_T *cl, char_u *name, size_t namelen)
3224{
3225 int idx;
3226 object_method_lookup(cl, name, namelen, &idx);
3227 return idx;
3228}
3229
3230/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003231 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
3232 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
3233 * object member variable.
3234 *
3235 * Returns a pointer to the member variable structure if variable is found.
3236 * Otherwise returns NULL. The member variable index is set in "*idx".
3237 */
3238 ocmember_T *
3239member_lookup(
3240 class_T *cl,
3241 vartype_T v_type,
3242 char_u *name,
3243 size_t namelen,
3244 int *idx)
3245{
3246 if (v_type == VAR_CLASS)
3247 return class_member_lookup(cl, name, namelen, idx);
3248 else
3249 return object_member_lookup(cl, name, namelen, idx);
3250}
3251
3252/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003253 * Find the class that defines the named member. Look up the hierarchy
3254 * starting at "cl".
3255 *
3256 * Return the class that defines the member "name", else NULL.
3257 * Fill in "p_m", if specified, for ocmember_T in found class.
3258 */
3259// NOTE: if useful for something could also indirectly return vartype and idx.
3260 static class_T *
3261class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m)
3262{
3263 class_T *cl_found = NULL;
3264 vartype_T vartype = VAR_UNKNOWN;
3265 ocmember_T *m_found = NULL;
3266
3267 len = len != 0 ? len : STRLEN(name);
3268
3269 // Loop assumes if member is not defined in "cl", then it is not
3270 // defined in any super class; the last class where it's found is the
3271 // class where it is defined. Once the vartype is found, the other
3272 // type is no longer checked.
3273 for (class_T *super = cl; super != NULL; super = super->class_extends)
3274 {
3275 class_T *cl_tmp = NULL;
3276 ocmember_T *m = NULL;
3277 if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT)
3278 {
3279 if ((m = object_member_lookup(super, name, len, NULL)) != NULL)
3280 {
3281 cl_tmp = super;
3282 vartype = VAR_OBJECT;
3283 }
3284 }
3285 if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS)
3286 {
3287 if (( m = class_member_lookup(super, name, len, NULL)) != NULL)
3288 {
3289 cl_tmp = super;
3290 vartype = VAR_OBJECT;
3291 }
3292 }
3293 if (cl_tmp == NULL)
3294 break; // member is not in this or any super class.
3295 cl_found = cl_tmp;
3296 m_found = m;
3297 }
3298 if (p_m != NULL)
3299 *p_m = m_found;
3300 return cl_found;
3301}
3302
3303/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02003304 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
3305 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
3306 *
3307 * Returns a pointer to the method structure if variable is found.
3308 * Otherwise returns NULL. The method variable index is set in "*idx".
3309 */
3310 ufunc_T *
3311method_lookup(
3312 class_T *cl,
3313 vartype_T v_type,
3314 char_u *name,
3315 size_t namelen,
3316 int *idx)
3317{
3318 if (v_type == VAR_CLASS)
3319 return class_method_lookup(cl, name, namelen, idx);
3320 else
3321 return object_method_lookup(cl, name, namelen, idx);
3322}
3323
3324/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00003325 * Return TRUE if current context "cctx_arg" is inside class "cl".
3326 * Return FALSE if not.
3327 */
3328 int
3329inside_class(cctx_T *cctx_arg, class_T *cl)
3330{
3331 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02003332 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003333 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00003334 return TRUE;
3335 return FALSE;
3336}
3337
3338/*
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01003339 * Return TRUE if object/class variable "m" is read-only.
3340 * Also give an error message.
3341 */
3342 int
3343oc_var_check_ro(class_T *cl, ocmember_T *m)
3344{
3345 if (m->ocm_flags & (OCMFLAG_FINAL | OCMFLAG_CONST))
3346 {
3347 semsg(_(e_cannot_change_readonly_variable_str_in_class_str),
3348 m->ocm_name, cl->class_name);
3349 return TRUE;
3350 }
3351 return FALSE;
3352}
3353
3354/*
3355 * Lock all the constant object variables. Called after creating and
3356 * initializing a new object.
3357 */
3358 void
3359obj_lock_const_vars(object_T *obj)
3360{
3361 for (int i = 0; i < obj->obj_class->class_obj_member_count; i++)
3362 {
3363 ocmember_T *ocm = &obj->obj_class->class_obj_members[i];
3364 if (ocm->ocm_flags & OCMFLAG_CONST)
3365 {
3366 typval_T *mtv = ((typval_T *)(obj + 1)) + i;
3367 item_lock(mtv, DICT_MAXNEST, TRUE, TRUE);
3368 }
3369 }
3370}
3371
3372/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003373 * Make a copy of an object.
3374 */
3375 void
3376copy_object(typval_T *from, typval_T *to)
3377{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003378 if (from->vval.v_object == NULL)
3379 to->vval.v_object = NULL;
3380 else
3381 {
3382 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003383 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003384 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003385}
3386
3387/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003388 * Make a copy of a class.
3389 */
3390 void
3391copy_class(typval_T *from, typval_T *to)
3392{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003393 if (from->vval.v_class == NULL)
3394 to->vval.v_class = NULL;
3395 else
3396 {
3397 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003398 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003399 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003400}
3401
3402/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003403 * Free the class "cl" and its contents.
3404 */
3405 static void
3406class_free(class_T *cl)
3407{
3408 // Freeing what the class contains may recursively come back here.
3409 // Clear "class_name" first, if it is NULL the class does not need to
3410 // be freed.
3411 VIM_CLEAR(cl->class_name);
3412
3413 class_unref(cl->class_extends);
3414
3415 for (int i = 0; i < cl->class_interface_count; ++i)
3416 {
3417 vim_free(((char_u **)cl->class_interfaces)[i]);
3418 if (cl->class_interfaces_cl[i] != NULL)
3419 class_unref(cl->class_interfaces_cl[i]);
3420 }
3421 vim_free(cl->class_interfaces);
3422 vim_free(cl->class_interfaces_cl);
3423
3424 itf2class_T *next;
3425 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
3426 {
3427 next = i2c->i2c_next;
3428 vim_free(i2c);
3429 }
3430
3431 for (int i = 0; i < cl->class_class_member_count; ++i)
3432 {
3433 ocmember_T *m = &cl->class_class_members[i];
3434 vim_free(m->ocm_name);
3435 vim_free(m->ocm_init);
3436 if (cl->class_members_tv != NULL)
3437 clear_tv(&cl->class_members_tv[i]);
3438 }
3439 vim_free(cl->class_class_members);
3440 vim_free(cl->class_members_tv);
3441
3442 for (int i = 0; i < cl->class_obj_member_count; ++i)
3443 {
3444 ocmember_T *m = &cl->class_obj_members[i];
3445 vim_free(m->ocm_name);
3446 vim_free(m->ocm_init);
3447 }
3448 vim_free(cl->class_obj_members);
3449
3450 for (int i = 0; i < cl->class_class_function_count; ++i)
3451 {
3452 ufunc_T *uf = cl->class_class_functions[i];
3453 func_clear_free(uf, FALSE);
3454 }
3455 vim_free(cl->class_class_functions);
3456
3457 for (int i = 0; i < cl->class_obj_method_count; ++i)
3458 {
3459 ufunc_T *uf = cl->class_obj_methods[i];
3460 func_clear_free(uf, FALSE);
3461 }
3462 vim_free(cl->class_obj_methods);
3463
3464 clear_type_list(&cl->class_type_list);
3465
3466 class_cleared(cl);
3467
3468 vim_free(cl);
3469}
3470
3471/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003472 * Unreference a class. Free it when the reference count goes down to zero.
3473 */
3474 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003475class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003476{
Bram Moolenaard505d172022-12-18 21:42:55 +00003477 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003478 class_free(cl);
3479}
3480
3481/*
3482 * Go through the list of all classes and free items without "copyID".
3483 */
3484 int
3485class_free_nonref(int copyID)
3486{
3487 int did_free = FALSE;
3488
3489 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003490 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003491 next_nonref_class = cl->class_next_used;
3492 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003493 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003494 // Free the class and items it contains.
3495 class_free(cl);
3496 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003497 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003498 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003499
3500 next_nonref_class = NULL;
3501 return did_free;
3502}
3503
3504 int
3505set_ref_in_classes(int copyID)
3506{
3507 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3508 set_ref_in_item_class(cl, copyID, NULL, NULL);
3509
3510 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003511}
3512
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003513static object_T *first_object = NULL;
3514
3515/*
3516 * Call this function when an object has been created. It will be added to the
3517 * list headed by "first_object".
3518 */
3519 void
3520object_created(object_T *obj)
3521{
3522 if (first_object != NULL)
3523 {
3524 obj->obj_next_used = first_object;
3525 first_object->obj_prev_used = obj;
3526 }
3527 first_object = obj;
3528}
3529
3530/*
3531 * Call this function when an object has been cleared and is about to be freed.
3532 * It is removed from the list headed by "first_object".
3533 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003534 static void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003535object_cleared(object_T *obj)
3536{
3537 if (obj->obj_next_used != NULL)
3538 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
3539 if (obj->obj_prev_used != NULL)
3540 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
3541 else if (first_object == obj)
3542 first_object = obj->obj_next_used;
3543}
3544
3545/*
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003546 * Free the contents of an object ignoring the reference count.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003547 */
3548 static void
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003549object_free_contents(object_T *obj)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003550{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003551 class_T *cl = obj->obj_class;
3552
3553 if (!cl)
3554 return;
3555
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003556 // Avoid a recursive call, it can happen if "obj" has a circular reference.
3557 obj->obj_refcount = INT_MAX;
3558
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003559 // the member values are just after the object structure
3560 typval_T *tv = (typval_T *)(obj + 1);
3561 for (int i = 0; i < cl->class_obj_member_count; ++i)
3562 clear_tv(tv + i);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003563}
3564
3565 static void
3566object_free_object(object_T *obj)
3567{
3568 class_T *cl = obj->obj_class;
3569
3570 if (!cl)
3571 return;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003572
3573 // Remove from the list headed by "first_object".
3574 object_cleared(obj);
3575
3576 vim_free(obj);
3577 class_unref(cl);
3578}
3579
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003580 static void
3581object_free(object_T *obj)
3582{
3583 if (in_free_unref_items)
3584 return;
3585
3586 object_free_contents(obj);
3587 object_free_object(obj);
3588}
3589
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003590/*
3591 * Unreference an object.
3592 */
3593 void
3594object_unref(object_T *obj)
3595{
3596 if (obj != NULL && --obj->obj_refcount <= 0)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003597 object_free(obj);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003598}
3599
3600/*
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003601 * Go through the list of all objects and free items without "copyID".
3602 */
3603 int
3604object_free_nonref(int copyID)
3605{
3606 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003607
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003608 for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003609 {
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003610 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3611 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003612 // Free the object contents. Object itself will be freed later.
3613 object_free_contents(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003614 did_free = TRUE;
3615 }
3616 }
3617
3618 return did_free;
3619}
3620
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003621 void
3622object_free_items(int copyID)
3623{
3624 object_T *obj_next;
3625
3626 for (object_T *obj = first_object; obj != NULL; obj = obj_next)
3627 {
3628 obj_next = obj->obj_next_used;
3629 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3630 object_free_object(obj);
3631 }
3632}
3633
LemonBoyafe04662023-08-23 21:08:11 +02003634/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003635 * Output message which takes a variable name and the class that defines it.
3636 * "cl" is that class where the name was found. Search "cl"'s hierarchy to
3637 * find the defining class.
3638 */
3639 void
3640emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl)
3641{
3642 ocmember_T *m;
3643 class_T *cl_def = class_defining_member(cl, name, len, &m);
3644 if (cl_def != NULL)
3645 semsg(_(msg), m->ocm_name, cl_def->class_name);
3646 else
3647 emsg(_(e_internal_error_please_report_a_bug));
3648}
3649
3650/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003651 * Echo a class or object method not found message.
3652 */
3653 void
3654method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3655{
3656 char_u *method_name = vim_strnsave(name, len);
3657 if ((v_type == VAR_OBJECT)
3658 && (class_method_idx(cl, name, len) >= 0))
3659 {
3660 // If this is a class method, then give a different error
3661 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003662 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003663 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003664 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003665 method_name, cl->class_name);
3666 }
3667 else if ((v_type == VAR_CLASS)
3668 && (object_method_idx(cl, name, len) >= 0))
3669 {
3670 // If this is an object method, then give a different error
3671 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003672 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003673 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003674 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003675 method_name, cl->class_name);
3676 }
3677 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003678 semsg(_(e_method_not_found_on_class_str_str), method_name,
3679 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003680 vim_free(method_name);
3681}
3682
3683/*
3684 * Echo a class or object member not found message.
3685 */
3686 void
3687member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3688{
3689 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
3690
3691 if (v_type == VAR_OBJECT)
3692 {
3693 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003694 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003695 varname, cl->class_name);
3696 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003697 semsg(_(e_variable_not_found_on_object_str_str), varname,
3698 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003699 }
3700 else
3701 {
3702 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003703 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003704 varname, cl->class_name);
3705 else
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01003706 {
3707 if (IS_ENUM(cl))
3708 semsg(_(e_enum_value_str_not_found_in_enum_str),
3709 varname, cl->class_name);
3710 else
3711 semsg(_(e_class_variable_str_not_found_in_class_str),
3712 varname, cl->class_name);
3713 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003714 }
3715 vim_free(varname);
3716}
3717
3718/*
Yegappan Lakshmanan4f32c832024-01-12 17:36:40 +01003719 * Compile all the class and object methods in "cl".
3720 */
3721 void
3722defcompile_class(class_T *cl)
3723{
3724 for (int loop = 1; loop <= 2; ++loop)
3725 {
3726 int func_count = loop == 1 ? cl->class_class_function_count
3727 : cl->class_obj_method_count;
3728 for (int i = 0; i < func_count; i++)
3729 {
3730 ufunc_T *ufunc = loop == 1 ? cl->class_class_functions[i]
3731 : cl->class_obj_methods[i];
Yegappan Lakshmanan1af0fbf2024-04-09 21:39:27 +02003732 // Don't compile abstract methods
3733 if (!IS_ABSTRACT_METHOD(ufunc))
3734 defcompile_function(ufunc, cl);
Yegappan Lakshmanan4f32c832024-01-12 17:36:40 +01003735 }
3736 }
3737}
3738
3739/*
3740 * Compile all the classes defined in the current script
3741 */
3742 void
3743defcompile_classes_in_script(void)
3744{
3745 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3746 {
3747 if (eval_variable(cl->class_name, 0, 0, NULL, NULL,
3748 EVAL_VAR_NOAUTOLOAD | EVAL_VAR_NO_FUNC) != FAIL)
3749 defcompile_class(cl);
3750 }
3751}
3752
3753/*
3754 * Returns TRUE if "name" is the name of a class. The typval for the class is
3755 * returned in "rettv".
3756 */
3757 int
3758is_class_name(char_u *name, typval_T *rettv)
3759{
3760 rettv->v_type = VAR_UNKNOWN;
3761
3762 if (eval_variable(name, 0, 0, rettv, NULL, EVAL_VAR_NOAUTOLOAD |
3763 EVAL_VAR_NO_FUNC) != FAIL)
3764 return rettv->v_type == VAR_CLASS;
3765 return FALSE;
3766}
3767
3768/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003769 * Calls the object builtin method "name" with arguments "argv". The value
3770 * returned by the builtin method is in "rettv". Returns OK or FAIL.
3771 */
3772 static int
3773object_call_builtin_method(
3774 object_T *obj,
3775 class_builtin_T builtin_method,
3776 int argc,
3777 typval_T *argv,
3778 typval_T *rettv)
3779{
3780 ufunc_T *uf;
3781 int midx;
3782
3783 if (obj == NULL)
3784 return FAIL;
3785
3786 uf = class_get_builtin_method(obj->obj_class, builtin_method, &midx);
3787 if (uf == NULL)
3788 return FAIL;
3789
3790 funccall_T *fc = create_funccal(uf, rettv);
3791 int r;
3792
3793 if (fc == NULL)
3794 return FAIL;
3795
3796 ++obj->obj_refcount;
3797
3798 r = call_def_function(uf, argc, argv, 0, NULL, obj, fc, rettv);
3799
3800 remove_funccal();
3801
3802 return r;
3803}
3804
3805/*
zeertzjqc029c132024-03-28 11:37:26 +01003806 * Calls the object "empty()" method and returns the method return value. In
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003807 * case of an error, returns TRUE.
3808 */
3809 int
3810object_empty(object_T *obj)
3811{
3812 typval_T rettv;
3813
3814 if (object_call_builtin_method(obj, CLASS_BUILTIN_EMPTY, 0, NULL, &rettv)
3815 == FAIL)
3816 return TRUE;
3817
3818 return tv_get_bool(&rettv);
3819}
3820
3821/*
3822 * Use the object "len()" method to get an object length. Returns 0 if the
3823 * method is not found or there is an error.
3824 */
3825 int
3826object_len(object_T *obj)
3827{
3828 typval_T rettv;
3829
3830 if (object_call_builtin_method(obj, CLASS_BUILTIN_LEN, 0, NULL, &rettv)
3831 == FAIL)
3832 return 0;
3833
3834 return tv_to_number(&rettv);
3835}
3836
3837/*
3838 * Return a textual representation of object "obj"
3839 */
3840 char_u *
3841object_string(
3842 object_T *obj,
3843 char_u *numbuf,
3844 int copyID,
3845 int echo_style,
3846 int restore_copyID,
3847 int composite_val)
3848{
3849 typval_T rettv;
3850
3851 if (object_call_builtin_method(obj, CLASS_BUILTIN_STRING, 0, NULL, &rettv)
3852 == OK
3853 && rettv.vval.v_string != NULL)
3854 return rettv.vval.v_string;
3855 else
3856 {
3857 garray_T ga;
3858 ga_init2(&ga, 1, 50);
3859
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003860 class_T *cl = obj == NULL ? NULL : obj->obj_class;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01003861 if (cl != NULL && IS_ENUM(cl))
3862 {
Yegappan Lakshmanan3cf121e2024-03-31 18:45:35 +02003863 ga_concat(&ga, (char_u *)"enum ");
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01003864 ga_concat(&ga, cl->class_name);
Yegappan Lakshmanan3cf121e2024-03-31 18:45:35 +02003865 char_u *enum_name = ((typval_T *)(obj + 1))->vval.v_string;
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01003866 ga_concat(&ga, (char_u *)".");
Yegappan Lakshmanan3cf121e2024-03-31 18:45:35 +02003867 ga_concat(&ga, enum_name);
Yegappan Lakshmanan3164cf82024-03-28 10:36:42 +01003868 }
Yegappan Lakshmanan3cf121e2024-03-31 18:45:35 +02003869 else
3870 {
3871 ga_concat(&ga, (char_u *)"object of ");
3872 ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]"
3873 : cl->class_name);
3874 }
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003875 if (cl != NULL)
3876 {
3877 ga_concat(&ga, (char_u *)" {");
3878 for (int i = 0; i < cl->class_obj_member_count; ++i)
3879 {
3880 if (i > 0)
3881 ga_concat(&ga, (char_u *)", ");
3882 ocmember_T *m = &cl->class_obj_members[i];
3883 ga_concat(&ga, m->ocm_name);
3884 ga_concat(&ga, (char_u *)": ");
3885 char_u *tf = NULL;
3886 ga_concat(&ga, echo_string_core(
3887 (typval_T *)(obj + 1) + i,
3888 &tf, numbuf, copyID, echo_style,
3889 restore_copyID, composite_val));
3890 vim_free(tf);
3891 }
3892 ga_concat(&ga, (char_u *)"}");
3893 }
3894 return ga.ga_data;
3895 }
3896}
3897
3898/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02003899 * Return TRUE when the class "cl", its base class or one of the implemented
3900 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02003901 */
3902 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003903class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02003904{
3905 if (cl == other_cl)
3906 return TRUE;
3907
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003908 // Recursively check the base classes.
3909 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02003910 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003911 if (cl == other_cl)
3912 return TRUE;
3913 // Check the implemented interfaces and the super interfaces
3914 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003915 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003916 class_T *intf = cl->class_interfaces_cl[i];
3917 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003918 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003919 if (intf == other_cl)
3920 return TRUE;
3921 // check the super interfaces
3922 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003923 }
3924 }
LemonBoyafe04662023-08-23 21:08:11 +02003925 }
3926
3927 return FALSE;
3928}
3929
3930/*
Ernie Rael2025af12023-12-12 16:58:00 +01003931 * "instanceof(object, classinfo, ...)" function
LemonBoyafe04662023-08-23 21:08:11 +02003932 */
3933 void
3934f_instanceof(typval_T *argvars, typval_T *rettv)
3935{
3936 typval_T *object_tv = &argvars[0];
3937 typval_T *classinfo_tv = &argvars[1];
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02003938 class_T *c;
LemonBoyafe04662023-08-23 21:08:11 +02003939
3940 rettv->vval.v_number = VVAL_FALSE;
3941
3942 if (check_for_object_arg(argvars, 0) == FAIL
Ernie Rael2025af12023-12-12 16:58:00 +01003943 || check_for_class_or_typealias_args(argvars, 1) == FAIL)
LemonBoyafe04662023-08-23 21:08:11 +02003944 return;
3945
Ernie Rael3da696d2023-09-19 20:14:18 +02003946 if (object_tv->vval.v_object == NULL)
3947 return;
3948
Ernie Rael2025af12023-12-12 16:58:00 +01003949 for (; classinfo_tv->v_type != VAR_UNKNOWN; ++classinfo_tv)
LemonBoyafe04662023-08-23 21:08:11 +02003950 {
Ernie Rael2025af12023-12-12 16:58:00 +01003951 if (classinfo_tv->v_type == VAR_TYPEALIAS)
3952 c = classinfo_tv->vval.v_typealias->ta_type->tt_class;
3953 else
3954 c = classinfo_tv->vval.v_class;
3955
3956 if (class_instance_of(object_tv->vval.v_object->obj_class, c))
LemonBoyafe04662023-08-23 21:08:11 +02003957 {
Ernie Rael2025af12023-12-12 16:58:00 +01003958 rettv->vval.v_number = VVAL_TRUE;
3959 return;
LemonBoyafe04662023-08-23 21:08:11 +02003960 }
3961 }
LemonBoyafe04662023-08-23 21:08:11 +02003962}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00003963
3964#endif // FEAT_EVAL