blob: e5d9aeb4baa0ddc6de089fa7a674632a93d1af6e [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.
171 * Returns FAIL otherwise, caller might need to free "init_expr".
172 */
173 static int
174add_member(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200175 garray_T *gap,
176 char_u *varname,
177 char_u *varname_end,
178 int has_public,
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100179 int has_final,
180 int has_const,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200181 int has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200182 type_T *type,
183 char_u *init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +0000184{
185 if (ga_grow(gap, 1) == FAIL)
186 return FAIL;
187 ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
188 m->ocm_name = vim_strnsave(varname, varname_end - varname);
=?UTF-8?q?Ola=20S=C3=B6der?=d8742472023-03-05 13:12:32 +0000189 m->ocm_access = has_public ? VIM_ACCESS_ALL
190 : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100191 if (has_final)
192 m->ocm_flags |= OCMFLAG_FINAL;
193 if (has_const)
194 m->ocm_flags |= OCMFLAG_CONST;
195 if (has_type)
196 m->ocm_flags |= OCMFLAG_HAS_TYPE;
Bram Moolenaard505d172022-12-18 21:42:55 +0000197 m->ocm_type = type;
198 if (init_expr != NULL)
199 m->ocm_init = init_expr;
200 ++gap->ga_len;
201 return OK;
202}
203
204/*
205 * Move the class or object members found while parsing a class into the class.
206 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000207 * "parent_members" points to the members in the parent class (if any)
208 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000209 * "members" will be set to the newly allocated array of members and
210 * "member_count" set to the number of members.
211 * Returns OK or FAIL.
212 */
213 static int
214add_members_to_class(
215 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000216 ocmember_T *parent_members,
217 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000218 ocmember_T **members,
219 int *member_count)
220{
Bram Moolenaar83677162023-01-08 19:54:10 +0000221 *member_count = parent_count + gap->ga_len;
222 *members = *member_count == 0 ? NULL
223 : ALLOC_MULT(ocmember_T, *member_count);
224 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000225 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000226 for (int i = 0; i < parent_count; ++i)
227 {
228 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000229 ocmember_T *m = *members + i;
230 *m = parent_members[i];
231 m->ocm_name = vim_strsave(m->ocm_name);
232 if (m->ocm_init != NULL)
233 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000234 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000235 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000236 // new members are moved
237 mch_memmove(*members + parent_count,
238 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000239 VIM_CLEAR(gap->ga_data);
240 return OK;
241}
242
243/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000244 * Convert a member index "idx" of interface "itf" to the member index of class
245 * "cl" implementing that interface.
246 */
247 int
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200248object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000249{
Ernie Rael18143d32023-09-04 22:30:41 +0200250 if (idx >= (is_method ? itf->class_obj_method_count
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200251 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000252 {
253 siemsg("index %d out of range for interface %s", idx, itf->class_name);
254 return 0;
255 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200256
257 // If "cl" is the interface or the class that is extended, then the method
258 // index can be used directly and there is no need to search for the method
259 // index in one of the child classes.
260 if (cl == itf)
261 return idx;
262
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200263 itf2class_T *i2c = NULL;
264 int searching = TRUE;
265 int method_offset = 0;
266
Ernie Raelcf138d42023-09-06 20:45:03 +0200267 for (class_T *super = cl; super != NULL && searching;
268 super = super->class_extends)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200269 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200270 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200271 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200272 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
273 {
274 searching = FALSE;
275 break;
276 }
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200277 }
278 if (searching && is_method)
279 // The parent class methods are stored after the current class
280 // methods.
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200281 method_offset += super->class_obj_method_count_child;
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200282 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000283 if (i2c == NULL)
284 {
285 siemsg("class %s not found on interface %s",
286 cl->class_name, itf->class_name);
287 return 0;
288 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +0200289
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200290 // A table follows the i2c for the class
291 int *table = (int *)(i2c + 1);
292 // "method_offset" is 0, if method is in the current class. If method
293 // is in a parent class, then it is non-zero.
294 return table[idx] + method_offset;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000295}
296
297/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200298 * Check whether a class named "extends_name" is present. If the class is
299 * valid, then "extends_clp" is set with the class pointer.
300 * Returns TRUE if the class name "extends_names" is a valid class.
301 */
302 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200303validate_extends_class(
304 char_u *extends_name,
305 class_T **extends_clp,
306 int is_class)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200307{
308 typval_T tv;
309 int success = FALSE;
310
311 tv.v_type = VAR_UNKNOWN;
312 if (eval_variable_import(extends_name, &tv) == FAIL)
313 {
314 semsg(_(e_class_name_not_found_str), extends_name);
315 return success;
316 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200317
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200318 if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
319 || (is_class
320 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
321 || (!is_class
322 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0))
323 // a interface cannot extend a class and a class cannot extend an
324 // interface.
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200325 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200326 else
327 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200328 class_T *extends_cl = tv.vval.v_class;
329 ++extends_cl->class_refcount;
330 *extends_clp = extends_cl;
331 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200332 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200333 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200334
335 return success;
336}
337
338/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200339 * Check method names in the parent class lineage to make sure the access is
340 * the same for overridden methods.
341 */
342 static int
343validate_extends_methods(
344 garray_T *objmethods_gap,
345 class_T *extends_cl)
346{
347 class_T *super = extends_cl;
348 int method_count = objmethods_gap->ga_len;
349 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
350
351 while (super != NULL)
352 {
353 int extends_method_count = super->class_obj_method_count_child;
354 if (extends_method_count == 0)
355 {
356 super = super->class_extends;
357 continue;
358 }
359
360 ufunc_T **extends_methods = super->class_obj_methods;
361
362 for (int i = 0; i < extends_method_count; i++)
363 {
364 char_u *pstr = extends_methods[i]->uf_name;
365 int extends_private = (*pstr == '_');
366 if (extends_private)
367 pstr++;
368
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200369 // When comparing the method names, ignore the access type (public
370 // and private methods are considered the same).
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200371 for (int j = 0; j < method_count; j++)
372 {
373 char_u *qstr = cl_fp[j]->uf_name;
374 int priv_method = (*qstr == '_');
375 if (priv_method)
376 qstr++;
377 if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
378 {
379 // Method access is different between the super class and
380 // the subclass
381 semsg(_(e_method_str_of_class_str_has_different_access),
382 cl_fp[j]->uf_name, super->class_name);
383 return FALSE;
384 }
385 }
386 }
387 super = super->class_extends;
388 }
389
390 return TRUE;
391}
392
393/*
394 * Check whether a object member variable in "objmembers_gap" is a duplicate of
395 * a member in any of the extended parent class lineage. Returns TRUE if there
396 * are no duplicates.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200397 */
398 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200399extends_check_dup_members(
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200400 garray_T *objmembers_gap,
401 class_T *extends_cl)
402{
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200403 int member_count = objmembers_gap->ga_len;
404 if (member_count == 0)
405 return TRUE;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200406
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200407 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
408
409 // Validate each member variable
410 for (int c_i = 0; c_i < member_count; c_i++)
411 {
412 class_T *p_cl = extends_cl;
413 ocmember_T *c_m = members + c_i;
414 char_u *pstr = (*c_m->ocm_name == '_')
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200415 ? c_m->ocm_name + 1 : c_m->ocm_name;
416
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200417 // Check in all the parent classes in the lineage
418 while (p_cl != NULL)
419 {
420 int p_member_count = p_cl->class_obj_member_count;
421 if (p_member_count == 0)
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200422 {
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200423 p_cl = p_cl->class_extends;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200424 continue;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200425 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200426 ocmember_T *p_members = p_cl->class_obj_members;
427
428 // Compare against all the members in the parent class
429 for (int p_i = 0; p_i < p_member_count; p_i++)
430 {
431 ocmember_T *p_m = p_members + p_i;
432 char_u *qstr = (*p_m->ocm_name == '_')
433 ? p_m->ocm_name + 1 : p_m->ocm_name;
434 if (STRCMP(pstr, qstr) == 0)
435 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200436 semsg(_(e_duplicate_variable_str), c_m->ocm_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200437 return FALSE;
438 }
439 }
440
441 p_cl = p_cl->class_extends;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200442 }
443 }
444
445 return TRUE;
446}
447
448/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200449 * Compare the variable type of interface variables in "objmembers_gap" against
450 * the variable in any of the extended super interface lineage. Used to
451 * compare the variable types when extending interfaces. Returns TRUE if the
452 * variable types are the same.
453 */
454 static int
455extends_check_intf_var_type(
456 garray_T *objmembers_gap,
457 class_T *extends_cl)
458{
459 int member_count = objmembers_gap->ga_len;
460 if (member_count == 0)
461 return TRUE;
462
463 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
464
465 // Validate each member variable
466 for (int c_i = 0; c_i < member_count; c_i++)
467 {
468 class_T *p_cl = extends_cl;
469 ocmember_T *c_m = members + c_i;
470 int var_found = FALSE;
471
472 // Check in all the parent classes in the lineage
473 while (p_cl != NULL && !var_found)
474 {
475 int p_member_count = p_cl->class_obj_member_count;
476 if (p_member_count == 0)
477 {
478 p_cl = p_cl->class_extends;
479 continue;
480 }
481 ocmember_T *p_members = p_cl->class_obj_members;
482
483 // Compare against all the members in the parent class
484 for (int p_i = 0; p_i < p_member_count; p_i++)
485 {
486 where_T where = WHERE_INIT;
487 ocmember_T *p_m = p_members + p_i;
488
489 if (STRCMP(p_m->ocm_name, c_m->ocm_name) != 0)
490 continue;
491
492 // Ensure the type is matching.
493 where.wt_func_name = (char *)c_m->ocm_name;
494 where.wt_kind = WT_MEMBER;
495
496 if (check_type(p_m->ocm_type, c_m->ocm_type, TRUE,
497 where) == FAIL)
498 return FALSE;
499
500 var_found = TRUE;
501 }
502
503 p_cl = p_cl->class_extends;
504 }
505 }
506
507 return TRUE;
508}
509
510/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200511 * When extending an abstract class, check whether all the abstract methods in
512 * the parent class are implemented. Returns TRUE if all the methods are
513 * implemented.
514 */
515 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200516validate_abstract_class_methods(
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200517 garray_T *classmethods_gap,
518 garray_T *objmethods_gap,
519 class_T *extends_cl)
520{
521 for (int loop = 1; loop <= 2; ++loop)
522 {
523 // loop == 1: check class methods
524 // loop == 2: check object methods
525 int extends_method_count = loop == 1
526 ? extends_cl->class_class_function_count
527 : extends_cl->class_obj_method_count;
528 if (extends_method_count == 0)
529 continue;
530
531 ufunc_T **extends_methods = loop == 1
532 ? extends_cl->class_class_functions
533 : extends_cl->class_obj_methods;
534
535 int method_count = loop == 1 ? classmethods_gap->ga_len
536 : objmethods_gap->ga_len;
537 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
538 ? classmethods_gap->ga_data
539 : objmethods_gap->ga_data);
540
541 for (int i = 0; i < extends_method_count; i++)
542 {
543 ufunc_T *uf = extends_methods[i];
Yegappan Lakshmanan1db15142023-09-19 20:34:05 +0200544 if (!IS_ABSTRACT_METHOD(uf))
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200545 continue;
546
547 int method_found = FALSE;
548
549 for (int j = 0; j < method_count; j++)
550 {
551 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
552 {
553 method_found = TRUE;
554 break;
555 }
556 }
557
558 if (!method_found)
559 {
560 semsg(_(e_abstract_method_str_not_found), uf->uf_name);
561 return FALSE;
562 }
563 }
564 }
565
566 return TRUE;
567}
568
569/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200570 * Returns TRUE if the interface variable "if_var" is present in the list of
571 * variables in "cl_mt" or in the parent lineage of one of the extended classes
572 * in "extends_cl". For a class variable, 'is_class_var' is TRUE.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200573 */
574 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200575intf_variable_present(
576 char_u *intf_class_name,
577 ocmember_T *if_var,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200578 ocmember_T *cl_mt,
579 int cl_member_count,
580 class_T *extends_cl)
581{
582 int variable_present = FALSE;
583
584 for (int cl_i = 0; cl_i < cl_member_count; ++cl_i)
585 {
586 ocmember_T *m = &cl_mt[cl_i];
587 where_T where = WHERE_INIT;
588
589 if (STRCMP(if_var->ocm_name, m->ocm_name) != 0)
590 continue;
591
592 // Ensure the access type is same
593 if (if_var->ocm_access != m->ocm_access)
594 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200595 semsg(_(e_variable_str_of_interface_str_has_different_access),
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200596 if_var->ocm_name, intf_class_name);
597 return FALSE;
598 }
599
600 // Ensure the type is matching.
601 if (m->ocm_type == &t_any)
602 {
603 // variable type is not specified. Use the variable type in the
604 // interface.
605 m->ocm_type = if_var->ocm_type;
606 }
607 else
608 {
609 where.wt_func_name = (char *)m->ocm_name;
610 where.wt_kind = WT_MEMBER;
611 if (check_type(if_var->ocm_type, m->ocm_type, TRUE,
612 where) == FAIL)
613 return FALSE;
614 }
615
616 variable_present = TRUE;
617 break;
618 }
619
620 if (!variable_present && extends_cl != NULL)
621 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200622 int ext_cl_count = extends_cl->class_obj_member_count;
623 ocmember_T *ext_cl_mt = extends_cl->class_obj_members;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200624 return intf_variable_present(intf_class_name, if_var,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200625 ext_cl_mt, ext_cl_count,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200626 extends_cl->class_extends);
627 }
628
629 return variable_present;
630}
631
632/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200633 * Check the variables of the interface class "ifcl" match object variables
634 * ("objmembers_gap") of a class.
635 * Returns TRUE if the object variables names are valid.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200636 */
637 static int
638validate_interface_variables(
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200639 char_u *intf_class_name,
640 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200641 garray_T *objmembers_gap,
642 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200643{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200644 int if_count = ifcl->class_obj_member_count;
645 if (if_count == 0)
646 return TRUE;
647
648 ocmember_T *if_ms = ifcl->class_obj_members;
649 ocmember_T *cl_ms = (ocmember_T *)(objmembers_gap->ga_data);
650 int cl_count = objmembers_gap->ga_len;
651 for (int if_i = 0; if_i < if_count; ++if_i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200652 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200653 if (!intf_variable_present(intf_class_name, &if_ms[if_i], cl_ms,
654 cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200655 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200656 semsg(_(e_variable_str_of_interface_str_not_implemented),
657 if_ms[if_i].ocm_name, intf_class_name);
658 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200659 }
660 }
661
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200662 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200663}
664
665/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200666 * Returns TRUE if the method signature of "if_method" and "cl_method" matches.
667 */
668 static int
669intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method)
670{
671 where_T where = WHERE_INIT;
672
673 // Ensure the type is matching.
674 where.wt_func_name = (char *)if_method->uf_name;
675 where.wt_kind = WT_METHOD;
676 if (check_type(if_method->uf_func_type, cl_method->uf_func_type, TRUE,
677 where) == FAIL)
678 return FALSE;
679
680 return TRUE;
681}
682
683/*
684 * Returns TRUE if the interface method "if_ufunc" is present in the list of
685 * methods in "cl_fp" or in the parent lineage of one of the extended classes
686 * in "extends_cl". For a class method, 'is_class_method' is TRUE.
687 */
688 static int
689intf_method_present(
690 ufunc_T *if_ufunc,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200691 ufunc_T **cl_fp,
692 int cl_count,
693 class_T *extends_cl)
694{
695 int method_present = FALSE;
696
697 for (int cl_i = 0; cl_i < cl_count; ++cl_i)
698 {
699 char_u *cl_name = cl_fp[cl_i]->uf_name;
700 if (STRCMP(if_ufunc->uf_name, cl_name) == 0)
701 {
702 // Ensure the type is matching.
703 if (!intf_method_type_matches(if_ufunc, cl_fp[cl_i]))
704 return FALSE;
705 method_present = TRUE;
706 break;
707 }
708 }
709
710 if (!method_present && extends_cl != NULL)
711 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200712 ufunc_T **ext_cl_fp = (ufunc_T **)(extends_cl->class_obj_methods);
713 int ext_cl_count = extends_cl->class_obj_method_count;
714 return intf_method_present(if_ufunc, ext_cl_fp, ext_cl_count,
715 extends_cl->class_extends);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200716 }
717
718 return method_present;
719}
720
721/*
722 * Validate that a new class implements all the class/instance methods in the
723 * interface "ifcl". The new class methods are in "classfunctions_gap" and the
724 * new object methods are in "objmemthods_gap". Also validates the method
725 * types.
726 * Returns TRUE if all the interface class/object methods are implemented in
727 * the new class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200728 */
729 static int
730validate_interface_methods(
731 char_u *intf_class_name,
732 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200733 garray_T *objmethods_gap,
734 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200735{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200736 int if_count = ifcl->class_obj_method_count;
737 if (if_count == 0)
738 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200739
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200740 ufunc_T **if_fp = ifcl->class_obj_methods;
741 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
742 int cl_count = objmethods_gap->ga_len;
743 for (int if_i = 0; if_i < if_count; ++if_i)
744 {
745 char_u *if_name = if_fp[if_i]->uf_name;
746
747 if (!intf_method_present(if_fp[if_i], cl_fp, cl_count, extends_cl))
748 {
749 semsg(_(e_method_str_of_interface_str_not_implemented),
750 if_name, intf_class_name);
751 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200752 }
753 }
754
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200755 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200756}
757
758/*
759 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200760 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200761 * object methods and object members in the new class are in
762 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
763 * "objmembers_gap" respectively.
764 */
765 static int
766validate_implements_classes(
767 garray_T *impl_gap,
768 class_T **intf_classes,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200769 garray_T *objmethods_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200770 garray_T *objmembers_gap,
771 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200772{
773 int success = TRUE;
774
775 for (int i = 0; i < impl_gap->ga_len && success; ++i)
776 {
777 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
778 typval_T tv;
779 tv.v_type = VAR_UNKNOWN;
780 if (eval_variable_import(impl, &tv) == FAIL)
781 {
782 semsg(_(e_interface_name_not_found_str), impl);
783 success = FALSE;
784 break;
785 }
786
787 if (tv.v_type != VAR_CLASS
788 || tv.vval.v_class == NULL
789 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
790 {
791 semsg(_(e_not_valid_interface_str), impl);
792 success = FALSE;
793 clear_tv(&tv);
794 break;
795 }
796
797 class_T *ifcl = tv.vval.v_class;
798 intf_classes[i] = ifcl;
799 ++ifcl->class_refcount;
800
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200801 // check the variables of the interface match the members of the class
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200802 success = validate_interface_variables(impl, ifcl, objmembers_gap,
803 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200804
805 // check the functions/methods of the interface match the
806 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200807 if (success)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200808 success = validate_interface_methods(impl, ifcl, objmethods_gap,
809 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200810 clear_tv(&tv);
811 }
812
813 return success;
814}
815
816/*
817 * Check no function argument name is used as a class member.
818 * (Object members are always accessed with "this." prefix, so no need
819 * to check them.)
820 */
821 static int
822check_func_arg_names(
823 garray_T *classfunctions_gap,
824 garray_T *objmethods_gap,
825 garray_T *classmembers_gap)
826{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200827 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200828 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200829 {
830 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
831
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200832 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200833 {
834 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
835
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200836 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200837 {
838 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
839 garray_T *mgap = classmembers_gap;
840
841 // Check all the class member names
842 for (int mi = 0; mi < mgap->ga_len; ++mi)
843 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200844 char_u *mname =
845 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200846 if (STRCMP(aname, mname) == 0)
847 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200848 if (uf->uf_script_ctx.sc_sid > 0)
849 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
850
851 semsg(_(e_argument_already_declared_in_class_str),
852 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200853
854 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200855 }
856 }
857 }
858 }
859 }
860
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200861 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200862}
863
864/*
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200865 * Returns TRUE if 'varname' is a reserved keyword name
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200866 */
867 static int
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200868is_reserved_varname(char_u *varname, char_u *varname_end)
869{
870 int reserved = FALSE;
871 char_u save_varname_end = *varname_end;
872 *varname_end = NUL;
873
874 reserved = check_reserved_name(varname, FALSE) == FAIL;
875
876 *varname_end = save_varname_end;
877
878 return reserved;
879}
880
881/*
882 * Returns TRUE if the variable "varname" is already defined either as a class
883 * variable or as an object variable.
884 */
885 static int
886is_duplicate_variable(
887 garray_T *class_members,
888 garray_T *obj_members,
889 char_u *varname,
890 char_u *varname_end)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200891{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200892 char_u *name = vim_strnsave(varname, varname_end - varname);
893 char_u *pstr = (*name == '_') ? name + 1 : name;
894 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200895
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200896 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200897 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200898 // loop == 1: class variables, loop == 2: object variables
899 garray_T *vgap = (loop == 1) ? class_members : obj_members;
900 for (int i = 0; i < vgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200901 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200902 ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i;
903 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1
904 : m->ocm_name;
905 if (STRCMP(pstr, qstr) == 0)
906 {
907 semsg(_(e_duplicate_variable_str), name);
908 dup = TRUE;
909 break;
910 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200911 }
912 }
913
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200914 vim_free(name);
915 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200916}
917
918/*
919 * Returns TRUE if the method "name" is already defined.
920 */
921 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200922is_duplicate_method(
923 garray_T *classmethods_gap,
924 garray_T *objmethods_gap,
925 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200926{
927 char_u *pstr = (*name == '_') ? name + 1 : name;
928
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200929 // loop 1: class methods, loop 2: object methods
930 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200931 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200932 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
933 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200934 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200935 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
936 char_u *qstr = *n == '_' ? n + 1 : n;
937 if (STRCMP(pstr, qstr) == 0)
938 {
939 semsg(_(e_duplicate_function_str), name);
940 return TRUE;
941 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200942 }
943 }
944
945 return FALSE;
946}
947
948/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200949 * Returns TRUE if the constructor is valid.
950 */
951 static int
952is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
953{
954 // Constructors are not allowed in abstract classes.
955 if (is_abstract)
956 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200957 emsg(_(e_cannot_define_new_method_in_abstract_class));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200958 return FALSE;
959 }
960 // A constructor is always static, no need to define it so.
961 if (has_static)
962 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200963 emsg(_(e_cannot_define_new_method_as_static));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200964 return FALSE;
965 }
966 // A return type should not be specified for the new()
967 // constructor method.
968 if (uf->uf_ret_type->tt_type != VAR_VOID)
969 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200970 emsg(_(e_cannot_use_a_return_type_with_new_method));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200971 return FALSE;
972 }
973 return TRUE;
974}
975
976/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200977 * Update the interface class lookup table for the member index on the
978 * interface to the member index in the class implementing the interface.
979 * And a lookup table for the object method index on the interface
980 * to the object method index in the class implementing the interface.
981 * This is also used for updating the lookup table for the extended class
982 * hierarchy.
983 */
984 static int
985update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200986 class_T *ifcl,
987 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200988 garray_T *objmethods,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200989 int pobj_method_offset)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200990{
991 if (ifcl == NULL)
992 return OK;
993
994 // Table for members.
995 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200996 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200997 if (if2cl == NULL)
998 return FAIL;
999 if2cl->i2c_next = ifcl->class_itf2class;
1000 ifcl->class_itf2class = if2cl;
1001 if2cl->i2c_class = cl;
1002 if2cl->i2c_is_method = FALSE;
1003
1004 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
1005 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
1006 {
1007 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001008 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001009 {
1010 int *table = (int *)(if2cl + 1);
1011 table[if_i] = cl_i;
1012 break;
1013 }
1014 }
1015
1016 // Table for methods.
1017 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001018 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001019 if (if2cl == NULL)
1020 return FAIL;
1021 if2cl->i2c_next = ifcl->class_itf2class;
1022 ifcl->class_itf2class = if2cl;
1023 if2cl->i2c_class = cl;
1024 if2cl->i2c_is_method = TRUE;
1025
1026 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
1027 {
1028 int done = FALSE;
1029 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
1030 {
1031 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001032 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001033 {
1034 int *table = (int *)(if2cl + 1);
1035 table[if_i] = cl_i;
1036 done = TRUE;
1037 break;
1038 }
1039 }
1040
1041 // extended class object method is not overridden by the child class.
1042 // Keep the method declared in one of the parent classes in the
1043 // lineage.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001044 if (!done)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001045 {
1046 // If "ifcl" is not the immediate parent of "cl", then search in
1047 // the intermediate parent classes.
1048 if (cl->class_extends != ifcl)
1049 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001050 class_T *parent = cl->class_extends;
1051 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001052
1053 while (!done && parent != NULL && parent != ifcl)
1054 {
1055
1056 for (int cl_i = 0;
1057 cl_i < parent->class_obj_method_count_child; ++cl_i)
1058 {
1059 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
1060 parent->class_obj_methods[cl_i]->uf_name)
1061 == 0)
1062 {
1063 int *table = (int *)(if2cl + 1);
1064 table[if_i] = method_offset + cl_i;
1065 done = TRUE;
1066 break;
1067 }
1068 }
1069 method_offset += parent->class_obj_method_count_child;
1070 parent = parent->class_extends;
1071 }
1072 }
1073
1074 if (!done)
1075 {
1076 int *table = (int *)(if2cl + 1);
1077 table[if_i] = pobj_method_offset + if_i;
1078 }
1079 }
1080 }
1081
1082 return OK;
1083}
1084
1085/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001086 * Update the member and object method lookup tables for a new class in the
1087 * interface class.
1088 * For each interface add a lookup table for the member index on the interface
1089 * to the member index in the new class. And a lookup table for the object
1090 * method index on the interface to the object method index in the new class.
1091 */
1092 static int
1093add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
1094{
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001095 // update the lookup table for all the implemented interfaces
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001096 for (int i = 0; i < cl->class_interface_count; ++i)
1097 {
1098 class_T *ifcl = cl->class_interfaces_cl[i];
1099
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001100 // update the lookup table for this interface and all its super
1101 // interfaces.
1102 while (ifcl != NULL)
1103 {
1104 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
1105 0) == FAIL)
1106 return FAIL;
1107 ifcl = ifcl->class_extends;
1108 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001109 }
1110
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001111 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001112 if (extends_cl != NULL)
1113 {
1114 class_T *pclass = extends_cl;
1115 int pobj_method_offset = objmethods_gap->ga_len;
1116
1117 // Update the entire lineage of extended classes.
1118 while (pclass != NULL)
1119 {
1120 if (update_member_method_lookup_table(pclass, cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001121 objmethods_gap, pobj_method_offset) == FAIL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001122 return FAIL;
1123
1124 pobj_method_offset += pclass->class_obj_method_count_child;
1125 pclass = pclass->class_extends;
1126 }
1127 }
1128
1129 return OK;
1130}
1131
1132/*
1133 * Add class members to a new class. Allocate a typval for each class member
1134 * and initialize it.
1135 */
1136 static void
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001137add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001138{
1139 // Allocate a typval for each class member and initialize it.
1140 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
1141 cl->class_class_member_count);
1142 if (cl->class_members_tv == NULL)
1143 return;
1144
1145 for (int i = 0; i < cl->class_class_member_count; ++i)
1146 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001147 ocmember_T *m = &cl->class_class_members[i];
1148 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001149 if (m->ocm_init != NULL)
1150 {
1151 typval_T *etv = eval_expr(m->ocm_init, eap);
1152 if (etv != NULL)
1153 {
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001154 if (m->ocm_type->tt_type == VAR_ANY
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001155 && !(m->ocm_flags & OCMFLAG_HAS_TYPE)
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001156 && etv->v_type != VAR_SPECIAL)
1157 // If the member variable type is not yet set, then use
1158 // the initialization expression type.
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001159 m->ocm_type = typval2type(etv, get_copyID(),
1160 type_list_gap,
1161 TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001162 *tv = *etv;
1163 vim_free(etv);
1164 }
1165 }
1166 else
1167 {
1168 // TODO: proper default value
1169 tv->v_type = m->ocm_type->tt_type;
1170 tv->vval.v_string = NULL;
1171 }
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001172 if (m->ocm_flags & OCMFLAG_CONST)
1173 item_lock(tv, DICT_MAXNEST, TRUE, TRUE);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001174 }
1175}
1176
1177/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001178 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001179 */
1180 static void
1181add_default_constructor(
1182 class_T *cl,
1183 garray_T *classfunctions_gap,
1184 garray_T *type_list_gap)
1185{
1186 garray_T fga;
1187
1188 ga_init2(&fga, 1, 1000);
1189 ga_concat(&fga, (char_u *)"new(");
1190 for (int i = 0; i < cl->class_obj_member_count; ++i)
1191 {
1192 if (i > 0)
1193 ga_concat(&fga, (char_u *)", ");
1194 ga_concat(&fga, (char_u *)"this.");
1195 ocmember_T *m = cl->class_obj_members + i;
1196 ga_concat(&fga, (char_u *)m->ocm_name);
1197 ga_concat(&fga, (char_u *)" = v:none");
1198 }
1199 ga_concat(&fga, (char_u *)")\nenddef\n");
1200 ga_append(&fga, NUL);
1201
1202 exarg_T fea;
1203 CLEAR_FIELD(fea);
1204 fea.cmdidx = CMD_def;
1205 fea.cmd = fea.arg = fga.ga_data;
1206
1207 garray_T lines_to_free;
1208 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1209
h-eastb895b0f2023-09-24 15:46:31 +02001210 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
1211 cl->class_obj_members, cl->class_obj_member_count);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001212
1213 ga_clear_strings(&lines_to_free);
1214 vim_free(fga.ga_data);
1215
1216 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1217 {
1218 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001219 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001220 ++classfunctions_gap->ga_len;
1221
1222 nf->uf_flags |= FC_NEW;
1223 nf->uf_ret_type = get_type_ptr(type_list_gap);
1224 if (nf->uf_ret_type != NULL)
1225 {
1226 nf->uf_ret_type->tt_type = VAR_OBJECT;
1227 nf->uf_ret_type->tt_class = cl;
1228 nf->uf_ret_type->tt_argcount = 0;
1229 nf->uf_ret_type->tt_args = NULL;
1230 }
1231 }
1232}
1233
1234/*
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001235 * Add the class methods and object methods to the new class "cl".
1236 * When extending a class "extends_cl", add the instance methods from the
1237 * parent class also.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001238 */
1239 static int
1240add_classfuncs_objmethods(
1241 class_T *cl,
1242 class_T *extends_cl,
1243 garray_T *classfunctions_gap,
1244 garray_T *objmethods_gap)
1245{
1246 // loop 1: class functions, loop 2: object methods
1247 for (int loop = 1; loop <= 2; ++loop)
1248 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001249 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1250 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001251 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001252 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001253 : &cl->class_obj_methods;
1254
1255 int parent_count = 0;
1256 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001257 // Include object methods from the parent.
1258 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001259 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001260 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001261 : extends_cl->class_obj_method_count;
1262
1263 *fcount = parent_count + gap->ga_len;
1264 if (*fcount == 0)
1265 {
1266 *fup = NULL;
1267 continue;
1268 }
1269 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1270 if (*fup == NULL)
1271 return FAIL;
1272
1273 if (gap->ga_len != 0)
1274 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
1275 vim_free(gap->ga_data);
1276 if (loop == 1)
1277 cl->class_class_function_count_child = gap->ga_len;
1278 else
1279 cl->class_obj_method_count_child = gap->ga_len;
1280
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001281 if (loop == 2)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001282 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001283 // Copy instance methods from the parent.
1284
1285 for (int i = 0; i < parent_count; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001286 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001287 // Can't use the same parent function, because "uf_class" is
1288 // different and compilation will have a different result.
1289 // Put them after the functions in the current class, object
1290 // methods may be overruled, then "super.Method()" is used to
1291 // find a method from the parent.
1292 ufunc_T *pf = (extends_cl->class_obj_methods)[i];
1293 (*fup)[gap->ga_len + i] = copy_function(pf);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001294
1295 // If the child class overrides a function from the parent
1296 // the signature must be equal.
1297 char_u *pname = pf->uf_name;
1298 for (int ci = 0; ci < gap->ga_len; ++ci)
1299 {
1300 ufunc_T *cf = (*fup)[ci];
1301 char_u *cname = cf->uf_name;
1302 if (STRCMP(pname, cname) == 0)
1303 {
1304 where_T where = WHERE_INIT;
1305 where.wt_func_name = (char *)pname;
1306 where.wt_kind = WT_METHOD;
1307 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1308 TRUE, where);
1309 }
1310 }
1311 }
1312 }
1313
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001314 // Set the class pointer on all the functions and object methods.
1315 for (int i = 0; i < *fcount; ++i)
1316 {
1317 ufunc_T *fp = (*fup)[i];
1318 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001319 if (i < gap->ga_len)
1320 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001321 if (loop == 2)
1322 fp->uf_flags |= FC_OBJECT;
1323 }
1324 }
1325
1326 return OK;
1327}
1328
1329/*
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001330 * Return the end of the class name starting at "arg". Valid characters in a
1331 * class name are alphanumeric characters and "_". Also handles imported class
1332 * names.
1333 */
1334 static char_u *
1335find_class_name_end(char_u *arg)
1336{
1337 char_u *end = arg;
1338
1339 while (ASCII_ISALNUM(*end) || *end == '_'
1340 || (*end == '.' && (ASCII_ISALNUM(end[1]) || end[1] == '_')))
1341 ++end;
1342
1343 return end;
1344}
1345
1346
1347/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001348 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001349 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001350 */
1351 void
1352ex_class(exarg_T *eap)
1353{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001354 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1355 long start_lnum = SOURCING_LNUM;
1356 char_u *arg = eap->arg;
1357 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001358
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001359 if (is_abstract)
1360 {
1361 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1362 {
1363 semsg(_(e_invalid_argument_str), arg);
1364 return;
1365 }
1366 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001367 is_class = TRUE;
1368 }
1369
1370 if (!current_script_is_vim9()
1371 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001372 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001373 {
1374 if (is_class)
1375 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1376 else
1377 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1378 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001379 }
1380
1381 if (!ASCII_ISUPPER(*arg))
1382 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001383 if (is_class)
1384 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1385 else
1386 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1387 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001388 return;
1389 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001390 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1391 if (!IS_WHITE_OR_NUL(*name_end))
1392 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001393 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001394 return;
1395 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001396 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001397
Bram Moolenaara86655a2023-01-12 17:06:27 +00001398 // "export class" gets used when creating the class, don't use "is_export"
1399 // for the items inside the class.
1400 int class_export = is_export;
1401 is_export = FALSE;
1402
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001403 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001404 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001405
Bram Moolenaar83677162023-01-08 19:54:10 +00001406 // Name for "extends BaseClass"
1407 char_u *extends = NULL;
1408
Bram Moolenaar94674f22023-01-06 18:42:20 +00001409 // Names for "implements SomeInterface"
1410 garray_T ga_impl;
1411 ga_init2(&ga_impl, sizeof(char_u *), 5);
1412
1413 arg = skipwhite(name_end);
1414 while (*arg != NUL && *arg != '#' && *arg != '\n')
1415 {
1416 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001417 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001418 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1419 {
1420 if (extends != NULL)
1421 {
1422 emsg(_(e_duplicate_extends));
1423 goto early_ret;
1424 }
1425 arg = skipwhite(arg + 7);
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001426
1427 char_u *end = find_class_name_end(arg);
Bram Moolenaar83677162023-01-08 19:54:10 +00001428 if (!IS_WHITE_OR_NUL(*end))
1429 {
1430 semsg(_(e_white_space_required_after_name_str), arg);
1431 goto early_ret;
1432 }
1433 extends = vim_strnsave(arg, end - arg);
1434 if (extends == NULL)
1435 goto early_ret;
1436
1437 arg = skipwhite(end + 1);
1438 }
1439 else if (STRNCMP(arg, "implements", 10) == 0
1440 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001441 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001442 if (!is_class)
1443 {
1444 emsg(_(e_interface_cannot_use_implements));
1445 goto early_ret;
1446 }
1447
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001448 if (ga_impl.ga_len > 0)
1449 {
1450 emsg(_(e_duplicate_implements));
1451 goto early_ret;
1452 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001453 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001454
1455 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001456 {
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001457 char_u *impl_end = find_class_name_end(arg);
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001458 if ((!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1459 || (*impl_end == ','
1460 && !IS_WHITE_OR_NUL(*(impl_end + 1))))
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001461 {
1462 semsg(_(e_white_space_required_after_name_str), arg);
1463 goto early_ret;
1464 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001465 if (impl_end - arg == 0)
1466 {
1467 emsg(_(e_missing_name_after_implements));
1468 goto early_ret;
1469 }
1470
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001471 char_u *iname = vim_strnsave(arg, impl_end - arg);
1472 if (iname == NULL)
1473 goto early_ret;
1474 for (int i = 0; i < ga_impl.ga_len; ++i)
1475 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1476 {
1477 semsg(_(e_duplicate_interface_after_implements_str),
1478 iname);
1479 vim_free(iname);
1480 goto early_ret;
1481 }
1482 if (ga_add_string(&ga_impl, iname) == FAIL)
1483 {
1484 vim_free(iname);
1485 goto early_ret;
1486 }
1487 if (*impl_end != ',')
1488 {
1489 arg = skipwhite(impl_end);
1490 break;
1491 }
1492 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001493 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001494 }
1495 else
1496 {
1497 semsg(_(e_trailing_characters_str), arg);
1498early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001499 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001500 ga_clear_strings(&ga_impl);
1501 return;
1502 }
1503 }
1504
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001505 garray_T type_list; // list of pointers to allocated types
1506 ga_init2(&type_list, sizeof(type_T *), 10);
1507
Bram Moolenaard505d172022-12-18 21:42:55 +00001508 // Growarray with class members declared in the class.
1509 garray_T classmembers;
1510 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1511
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001512 // Growarray with functions declared in the class.
1513 garray_T classfunctions;
1514 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001515
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001516 // Growarray with object members declared in the class.
1517 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001518 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001519
1520 // Growarray with object methods declared in the class.
1521 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001522 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001523
1524 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001525 * Go over the body of the class/interface until "endclass" or
1526 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001527 */
1528 char_u *theline = NULL;
1529 int success = FALSE;
1530 for (;;)
1531 {
1532 vim_free(theline);
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001533 theline = eap->ea_getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001534 if (theline == NULL)
1535 break;
1536 char_u *line = skipwhite(theline);
1537
Bram Moolenaar418b5472022-12-20 13:38:22 +00001538 // Skip empty and comment lines.
1539 if (*line == NUL)
1540 continue;
1541 if (*line == '#')
1542 {
1543 if (vim9_bad_comment(line))
1544 break;
1545 continue;
1546 }
1547
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001548 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001549 char *end_name = is_class ? "endclass" : "endinterface";
1550 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001551 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001552 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001553 semsg(_(e_command_cannot_be_shortened_str), line);
1554 else if (*p == '|' || !ends_excmd2(line, p))
1555 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001556 else
1557 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001558 break;
1559 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001560 char *wrong_name = is_class ? "endinterface" : "endclass";
1561 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1562 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001563 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001564 break;
1565 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001566
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001567 int has_public = FALSE;
1568 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001569 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001570 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001571 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001572 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001573 break;
1574 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001575 if (!is_class)
1576 {
Yegappan Lakshmananb90e3bc2023-09-28 23:06:48 +02001577 emsg(_(e_public_variable_not_supported_in_interface));
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001578 break;
1579 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001580 has_public = TRUE;
1581 p = skipwhite(line + 6);
1582
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001583 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "static", 6) != 0
1584 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001585 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001586 emsg(_(e_public_must_be_followed_by_var_static_final_or_const));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001587 break;
1588 }
1589 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001590
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001591 int abstract_method = FALSE;
1592 char_u *pa = p;
1593 if (checkforcmd(&p, "abstract", 3))
1594 {
1595 if (STRNCMP(pa, "abstract", 8) != 0)
1596 {
1597 semsg(_(e_command_cannot_be_shortened_str), pa);
1598 break;
1599 }
1600
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01001601 if (!is_class)
1602 {
1603 // "abstract" not supported in an interface
1604 emsg(_(e_abstract_cannot_be_used_in_interface));
1605 break;
1606 }
1607
1608 if (!is_abstract)
1609 {
1610 semsg(_(e_abstract_method_in_concrete_class), pa);
1611 break;
1612 }
1613
Yegappan Lakshmanan5a539252023-11-04 09:42:46 +01001614 p = skipwhite(pa + 8);
1615 if (STRNCMP(p, "def", 3) != 0)
1616 {
1617 emsg(_(e_abstract_must_be_followed_by_def));
1618 break;
1619 }
1620
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01001621 abstract_method = TRUE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001622 }
1623
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001624 int has_static = FALSE;
1625 char_u *ps = p;
1626 if (checkforcmd(&p, "static", 4))
1627 {
1628 if (STRNCMP(ps, "static", 6) != 0)
1629 {
1630 semsg(_(e_command_cannot_be_shortened_str), ps);
1631 break;
1632 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001633
1634 if (!is_class)
1635 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001636 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001637 break;
1638 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001639 has_static = TRUE;
1640 p = skipwhite(ps + 6);
Doug Kearns74da0ee2023-12-14 20:26:26 +01001641
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001642 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "def", 3) != 0
1643 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Doug Kearns74da0ee2023-12-14 20:26:26 +01001644 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001645 emsg(_(e_static_must_be_followed_by_var_def_final_or_const));
Doug Kearns74da0ee2023-12-14 20:26:26 +01001646 break;
1647 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001648 }
1649
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001650 int has_final = FALSE;
1651 int has_var = FALSE;
1652 int has_const = FALSE;
1653 if (checkforcmd(&p, "var", 3))
1654 has_var = TRUE;
1655 else if (checkforcmd(&p, "final", 5))
1656 {
1657 if (!is_class)
1658 {
1659 emsg(_(e_final_variable_not_supported_in_interface));
1660 break;
1661 }
1662 has_final = TRUE;
1663 }
1664 else if (checkforcmd(&p, "const", 5))
1665 {
1666 if (!is_class)
1667 {
1668 emsg(_(e_const_variable_not_supported_in_interface));
1669 break;
1670 }
1671 has_const = TRUE;
1672 }
1673 p = skipwhite(p);
1674
Bram Moolenaard505d172022-12-18 21:42:55 +00001675 // object members (public, read access, private):
Doug Kearns74da0ee2023-12-14 20:26:26 +01001676 // "var _varname"
1677 // "var varname"
1678 // "public var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001679 // "final _varname"
1680 // "final varname"
1681 // "public final varname"
1682 // "const _varname"
1683 // "const varname"
1684 // "public const varname"
Doug Kearns74da0ee2023-12-14 20:26:26 +01001685 // class members (public, read access, private):
1686 // "static var _varname"
1687 // "static var varname"
1688 // "public static var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001689 // "static final _varname"
1690 // "static final varname"
1691 // "public static final varname"
1692 // "static const _varname"
1693 // "static const varname"
1694 // "public static const varname"
1695 if (has_var || has_final || has_const)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001696 {
Doug Kearns74da0ee2023-12-14 20:26:26 +01001697 char_u *varname = p;
Bram Moolenaard505d172022-12-18 21:42:55 +00001698 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001699 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001700 char_u *init_expr = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001701 int has_type = FALSE;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001702
Doug Kearns74da0ee2023-12-14 20:26:26 +01001703 if (!eval_isnamec1(*p))
1704 {
1705 if (has_static)
1706 semsg(_(e_invalid_class_variable_declaration_str), line);
1707 else
1708 semsg(_(e_invalid_object_variable_declaration_str), line);
1709 break;
1710 }
1711
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001712 if (!is_class && *varname == '_')
1713 {
1714 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01001715 semsg(_(e_protected_variable_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001716 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001717 break;
1718 }
1719
Bram Moolenaard505d172022-12-18 21:42:55 +00001720 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001721 &varname_end, &has_type, &type_list, &type,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001722 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001723 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001724 if (is_reserved_varname(varname, varname_end))
1725 {
1726 vim_free(init_expr);
1727 break;
1728 }
1729 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1730 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001731 {
1732 vim_free(init_expr);
1733 break;
1734 }
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001735 if (add_member(has_static ? &classmembers : &objmembers, varname,
1736 varname_end, has_public, has_final, has_const,
1737 has_type, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001738 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001739 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001740 break;
1741 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001742 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001743
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001744 // constructors:
1745 // def new()
1746 // enddef
1747 // def newOther()
1748 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001749 // object methods and class functions:
1750 // def SomeMethod()
1751 // enddef
1752 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001753 // enddef
1754 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001755 // def <Tval> someMethod()
1756 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001757 else if (checkforcmd(&p, "def", 3))
1758 {
1759 exarg_T ea;
1760 garray_T lines_to_free;
1761
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001762 if (has_public)
1763 {
1764 // "public" keyword is not supported when defining an object or
1765 // class method
1766 emsg(_(e_public_keyword_not_supported_for_method));
1767 break;
1768 }
1769
1770 if (*p == NUL)
1771 {
1772 // No method name following def
1773 semsg(_(e_not_valid_command_in_class_str), line);
1774 break;
1775 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001776
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01001777 if (*p == '_' && *(p + 1) == '_')
1778 {
1779 // double underscore prefix for a method name is currently
1780 // reserved. This could be used in the future to support
1781 // object methods called by Vim builtin functions.
1782 semsg(_(e_cannot_use_reserved_name_str), p);
1783 break;
1784 }
1785
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001786 CLEAR_FIELD(ea);
1787 ea.cmd = line;
1788 ea.arg = p;
1789 ea.cmdidx = CMD_def;
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001790 ea.ea_getline = eap->ea_getline;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001791 ea.cookie = eap->cookie;
1792
1793 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001794 int class_flags;
1795 if (is_class)
1796 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
1797 else
1798 class_flags = CF_INTERFACE;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001799 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02001800 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001801 ga_clear_strings(&lines_to_free);
1802
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001803 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001804 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001805 char_u *name = uf->uf_name;
1806 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001807
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001808 if (!is_class && *name == '_')
1809 {
1810 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01001811 semsg(_(e_protected_method_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001812 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001813 func_clear_free(uf, FALSE);
1814 break;
1815 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001816 if (is_new && !is_valid_constructor(uf, is_abstract,
1817 has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001818 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001819 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001820 break;
1821 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001822
Bram Moolenaar58b40092023-01-11 15:59:05 +00001823 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001824 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001825 {
1826 success = FALSE;
1827 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001828 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001829 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001830
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001831 garray_T *fgap = has_static || is_new
1832 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001833 if (ga_grow(fgap, 1) == OK)
1834 {
1835 if (is_new)
1836 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001837
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001838 if (abstract_method)
1839 uf->uf_flags |= FC_ABSTRACT;
1840
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001841 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1842 ++fgap->ga_len;
1843 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001844 }
1845 }
1846
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001847 else
1848 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001849 if (is_class)
1850 semsg(_(e_not_valid_command_in_class_str), line);
1851 else
1852 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001853 break;
1854 }
1855 }
1856 vim_free(theline);
1857
Bram Moolenaar83677162023-01-08 19:54:10 +00001858 class_T *extends_cl = NULL; // class from "extends" argument
1859
1860 /*
1861 * Check a few things before defining the class.
1862 */
1863
1864 // Check the "extends" class is valid.
1865 if (success && extends != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001866 success = validate_extends_class(extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00001867 VIM_CLEAR(extends);
1868
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001869 // Check the new object methods to make sure their access (public or
1870 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001871 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001872 success = validate_extends_methods(&objmethods, extends_cl);
1873
1874 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001875 // variables in the extended class lineage. If an interface is extending
1876 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001877 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001878 {
1879 if (is_class)
1880 success = extends_check_dup_members(&objmembers, extends_cl);
1881 else
1882 success = extends_check_intf_var_type(&objmembers, extends_cl);
1883 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001884
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001885 // When extending an abstract class, make sure all the abstract methods in
1886 // the parent class are implemented. If the current class is an abstract
1887 // class, then there is no need for this check.
1888 if (success && !is_abstract && extends_cl != NULL
1889 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001890 success = validate_abstract_class_methods(&classfunctions,
1891 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001892
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001893 class_T **intf_classes = NULL;
1894
Bram Moolenaar83677162023-01-08 19:54:10 +00001895 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001896 if (success && ga_impl.ga_len > 0)
1897 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001898 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1899
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001900 success = validate_implements_classes(&ga_impl, intf_classes,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02001901 &objmethods, &objmembers, extends_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001902 }
1903
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001904 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001905 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001906 success = check_func_arg_names(&classfunctions, &objmethods,
1907 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001908
Bram Moolenaareb533502022-12-14 15:06:11 +00001909 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001910 if (success)
1911 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001912 // "endclass" encountered without failures: Create the class.
1913
Bram Moolenaareb533502022-12-14 15:06:11 +00001914 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001915 if (cl == NULL)
1916 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001917 if (!is_class)
1918 cl->class_flags = CLASS_INTERFACE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001919 else if (is_abstract)
1920 cl->class_flags = CLASS_ABSTRACT;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001921
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001922 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001923 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001924 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001925 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001926
Bram Moolenaard0200c82023-01-28 15:19:40 +00001927 if (extends_cl != NULL)
1928 {
1929 cl->class_extends = extends_cl;
1930 extends_cl->class_flags |= CLASS_EXTENDED;
1931 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001932
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001933 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00001934 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001935 NULL,
1936 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00001937 &cl->class_class_members,
1938 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001939 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001940 extends_cl == NULL ? NULL
1941 : extends_cl->class_obj_members,
1942 extends_cl == NULL ? 0
1943 : extends_cl->class_obj_member_count,
1944 &cl->class_obj_members,
1945 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001946 goto cleanup;
1947
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001948 if (ga_impl.ga_len > 0)
1949 {
1950 // Move the "implements" names into the class.
1951 cl->class_interface_count = ga_impl.ga_len;
1952 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1953 if (cl->class_interfaces == NULL)
1954 goto cleanup;
1955 for (int i = 0; i < ga_impl.ga_len; ++i)
1956 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1957 VIM_CLEAR(ga_impl.ga_data);
1958 ga_impl.ga_len = 0;
1959
Bram Moolenaard0200c82023-01-28 15:19:40 +00001960 cl->class_interfaces_cl = intf_classes;
1961 intf_classes = NULL;
1962 }
1963
1964 if (cl->class_interface_count > 0 || extends_cl != NULL)
1965 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001966 // Add a method and member lookup table to each of the interface
1967 // classes.
1968 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1969 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001970 }
1971
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001972 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001973 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001974 add_class_members(cl, eap, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001975
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001976 int have_new = FALSE;
1977 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001978 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001979 {
1980 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1981 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001982 {
1983 have_new = TRUE;
1984 break;
1985 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001986 }
1987
1988 if (have_new)
1989 // The return type of new() is an object of class "cl"
1990 class_func->uf_ret_type->tt_class = cl;
1991 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001992 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001993 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001994
Bram Moolenaar58b40092023-01-11 15:59:05 +00001995 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001996 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1997 &objmethods) == FAIL)
1998 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001999
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002000 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00002001 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002002 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00002003 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002004 cl->class_type_list = type_list;
2005
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002006 class_created(cl);
2007
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002008 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00002009 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002010
2011 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00002012 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02002013 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002014 typval_T tv;
2015 tv.v_type = VAR_CLASS;
2016 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00002017 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00002018 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002019 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00002020 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002021 return;
2022 }
2023
2024cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00002025 if (cl != NULL)
2026 {
2027 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002028 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002029 if (cl->class_interfaces != NULL)
2030 {
2031 for (int i = 0; i < cl->class_interface_count; ++i)
2032 vim_free(cl->class_interfaces[i]);
2033 vim_free(cl->class_interfaces);
2034 }
2035 if (cl->class_interfaces_cl != NULL)
2036 {
2037 for (int i = 0; i < cl->class_interface_count; ++i)
2038 class_unref(cl->class_interfaces_cl[i]);
2039 vim_free(cl->class_interfaces_cl);
2040 }
Bram Moolenaareb533502022-12-14 15:06:11 +00002041 vim_free(cl->class_obj_members);
2042 vim_free(cl->class_obj_methods);
2043 vim_free(cl);
2044 }
2045
Bram Moolenaar83677162023-01-08 19:54:10 +00002046 vim_free(extends);
2047 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002048
2049 if (intf_classes != NULL)
2050 {
2051 for (int i = 0; i < ga_impl.ga_len; ++i)
2052 class_unref(intf_classes[i]);
2053 vim_free(intf_classes);
2054 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00002055 ga_clear_strings(&ga_impl);
2056
Bram Moolenaard505d172022-12-18 21:42:55 +00002057 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002058 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002059 garray_T *gap = round == 1 ? &classmembers : &objmembers;
2060 if (gap->ga_len == 0 || gap->ga_data == NULL)
2061 continue;
2062
2063 for (int i = 0; i < gap->ga_len; ++i)
2064 {
2065 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2066 vim_free(m->ocm_name);
2067 vim_free(m->ocm_init);
2068 }
2069 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002070 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002071
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002072 for (int i = 0; i < objmethods.ga_len; ++i)
2073 {
2074 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2075 func_clear_free(uf, FALSE);
2076 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002077 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002078
2079 for (int i = 0; i < classfunctions.ga_len; ++i)
2080 {
2081 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2082 func_clear_free(uf, FALSE);
2083 }
2084 ga_clear(&classfunctions);
2085
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002086 clear_type_list(&type_list);
2087}
2088
2089/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002090 * Find member "name" in class "cl", set "member_idx" to the member index and
2091 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002092 * When "is_object" is TRUE, then look for object members. Otherwise look for
2093 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002094 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002095 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002096 */
2097 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002098oc_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002099 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002100 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002101 char_u *name,
2102 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002103 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002104{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002105 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002106 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002107
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002108 *member_idx = -1; // not found (yet)
2109
2110 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2111 member_idx);
2112 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002113 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002114 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2115 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002116 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002117 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002118
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002119 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002120}
2121
2122/*
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002123 * Given a class or object variable index, return the variable type
2124 */
2125 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002126oc_member_type_by_idx(
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002127 class_T *cl,
2128 int is_object,
2129 int member_idx)
2130{
2131 ocmember_T *m;
2132 int member_count;
2133
2134 if (is_object)
2135 {
2136 m = cl->class_obj_members;
2137 member_count = cl->class_obj_member_count;
2138 }
2139 else
2140 {
2141 m = cl->class_class_members;
2142 member_count = cl->class_class_member_count;
2143 }
2144
2145 if (member_idx >= member_count)
2146 return NULL;
2147
2148 return m[member_idx].ocm_type;
2149}
2150
2151/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002152 * Handle ":enum" up to ":endenum".
2153 */
2154 void
2155ex_enum(exarg_T *eap UNUSED)
2156{
2157 // TODO
2158}
2159
2160/*
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002161 * Type aliases (:type)
2162 */
2163
2164 void
2165typealias_free(typealias_T *ta)
2166{
2167 // ta->ta_type is freed in clear_type_list()
2168 vim_free(ta->ta_name);
2169 vim_free(ta);
2170}
2171
2172 void
2173typealias_unref(typealias_T *ta)
2174{
2175 if (ta != NULL && --ta->ta_refcount <= 0)
2176 typealias_free(ta);
2177}
2178
2179/*
2180 * Handle ":type". Create an alias for a type specification.
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002181 */
2182 void
2183ex_type(exarg_T *eap UNUSED)
2184{
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002185 char_u *arg = eap->arg;
2186
2187 if (!current_script_is_vim9()
2188 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002189 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002190 {
2191 emsg(_(e_type_can_only_be_defined_in_vim9_script));
2192 return;
2193 }
2194
2195 if (*arg == NUL)
2196 {
2197 emsg(_(e_missing_typealias_name));
2198 return;
2199 }
2200
2201 if (!ASCII_ISUPPER(*arg))
2202 {
2203 semsg(_(e_type_name_must_start_with_uppercase_letter_str), arg);
2204 return;
2205 }
2206
2207 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
2208 if (!IS_WHITE_OR_NUL(*name_end))
2209 {
2210 semsg(_(e_white_space_required_after_name_str), arg);
2211 return;
2212 }
2213 char_u *name_start = arg;
2214
2215 arg = skipwhite(name_end);
2216 if (*arg != '=')
2217 {
2218 semsg(_(e_missing_equal_str), arg);
2219 return;
2220 }
2221 if (!IS_WHITE_OR_NUL(*(arg + 1)))
2222 {
2223 semsg(_(e_white_space_required_after_str_str), "=", arg);
2224 return;
2225 }
2226 arg++;
2227 arg = skipwhite(arg);
2228
2229 if (*arg == NUL)
2230 {
2231 emsg(_(e_missing_typealias_type));
2232 return;
2233 }
2234
2235 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
2236 type_T *type = parse_type(&arg, &si->sn_type_list, TRUE);
2237 if (type == NULL)
2238 return;
2239
2240 if (*arg != NUL)
2241 {
2242 // some text after the type
2243 semsg(_(e_trailing_characters_str), arg);
2244 return;
2245 }
2246
2247 int cc = *name_end;
2248 *name_end = NUL;
2249
2250 typval_T tv;
2251 tv.v_type = VAR_UNKNOWN;
2252 if (eval_variable_import(name_start, &tv) == OK)
2253 {
2254 if (tv.v_type == VAR_TYPEALIAS)
2255 semsg(_(e_typealias_already_exists_for_str), name_start);
2256 else
2257 semsg(_(e_redefining_script_item_str), name_start);
2258 clear_tv(&tv);
2259 goto done;
2260 }
2261
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02002262 // Create a script-local variable for the type alias.
2263 if (type->tt_type != VAR_OBJECT)
2264 {
2265 tv.v_type = VAR_TYPEALIAS;
2266 tv.v_lock = 0;
2267 tv.vval.v_typealias = ALLOC_CLEAR_ONE(typealias_T);
2268 ++tv.vval.v_typealias->ta_refcount;
2269 tv.vval.v_typealias->ta_name = vim_strsave(name_start);
2270 tv.vval.v_typealias->ta_type = type;
2271 }
2272 else
2273 {
2274 // When creating a type alias for a class, use the class type itself to
2275 // create the type alias variable. This is needed to use the type
2276 // alias to invoke class methods (e.g. new()) and use class variables.
2277 tv.v_type = VAR_CLASS;
2278 tv.v_lock = 0;
2279 tv.vval.v_class = type->tt_class;
2280 ++tv.vval.v_class->class_refcount;
2281 }
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002282 set_var_const(name_start, current_sctx.sc_sid, NULL, &tv, FALSE,
2283 ASSIGN_CONST | ASSIGN_FINAL, 0);
2284
2285done:
2286 *name_end = cc;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002287}
2288
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002289/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002290 * Returns OK if a member variable named "name" is present in the class "cl".
2291 * Otherwise returns FAIL. If found, the member variable typval is set in
2292 * "rettv". If "is_object" is TRUE, then the object member variable table is
2293 * searched. Otherwise the class member variable table is searched.
2294 */
2295 static int
2296get_member_tv(
2297 class_T *cl,
2298 int is_object,
2299 char_u *name,
2300 size_t namelen,
2301 typval_T *rettv)
2302{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002303 ocmember_T *m;
2304 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002305
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002306 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2307 &m_idx);
2308 if (m == NULL)
2309 return FAIL;
2310
2311 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002312 {
Ernie Rael03042a22023-11-11 08:53:32 +01002313 emsg_var_cl_define(e_cannot_access_protected_variable_str,
Ernie Raele6c9aa52023-10-06 19:55:52 +02002314 m->ocm_name, 0, cl);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002315 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002316 }
2317
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002318 if (is_object)
2319 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002320 // The object only contains a pointer to the class, the member values
2321 // array follows right after that.
2322 object_T *obj = rettv->vval.v_object;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002323 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2324 copy_tv(tv, rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002325 object_unref(obj);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002326 }
2327 else
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002328 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002329 copy_tv(&cl->class_members_tv[m_idx], rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002330 class_unref(cl);
2331 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002332
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002333 return OK;
2334}
2335
2336/*
2337 * Call an object or class method "name" in class "cl". The method return
2338 * value is returned in "rettv".
2339 */
2340 static int
2341call_oc_method(
2342 class_T *cl,
2343 char_u *name,
2344 size_t len,
2345 char_u *name_end,
2346 evalarg_T *evalarg,
2347 char_u **arg,
2348 typval_T *rettv)
2349{
2350 ufunc_T *fp;
2351 typval_T argvars[MAX_FUNC_ARGS + 1];
2352 int argcount = 0;
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002353 ocmember_T *ocm = NULL;
2354 int m_idx;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002355
2356 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
2357 if (fp == NULL)
2358 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002359 // could be an object or class funcref variable
2360 ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
2361 if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
2362 {
2363 method_not_found_msg(cl, rettv->v_type, name, len);
2364 return FAIL;
2365 }
2366
2367 if (rettv->v_type == VAR_OBJECT)
2368 {
2369 // funcref object variable
2370 object_T *obj = rettv->vval.v_object;
2371 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2372 copy_tv(tv, rettv);
2373 }
2374 else
2375 // funcref class variable
2376 copy_tv(&cl->class_members_tv[m_idx], rettv);
2377 *arg = name_end;
2378 return OK;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002379 }
2380
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002381 if (ocm == NULL && *fp->uf_name == '_')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002382 {
2383 // Cannot access a private method outside of a class
Ernie Rael03042a22023-11-11 08:53:32 +01002384 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002385 return FAIL;
2386 }
2387
2388 char_u *argp = name_end;
Ernie Raelb077b582023-12-14 20:11:44 +01002389 int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount, FALSE);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002390 if (ret == FAIL)
2391 return FAIL;
2392
2393 funcexe_T funcexe;
2394 CLEAR_FIELD(funcexe);
2395 funcexe.fe_evaluate = TRUE;
2396 if (rettv->v_type == VAR_OBJECT)
2397 {
2398 funcexe.fe_object = rettv->vval.v_object;
2399 ++funcexe.fe_object->obj_refcount;
2400 }
2401
2402 // Clear the class or object after calling the function, in
2403 // case the refcount is one.
2404 typval_T tv_tofree = *rettv;
2405 rettv->v_type = VAR_UNKNOWN;
2406
2407 // Call the user function. Result goes into rettv;
2408 int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
2409 NULL);
2410
2411 // Clear the previous rettv and the arguments.
2412 clear_tv(&tv_tofree);
2413 for (int idx = 0; idx < argcount; ++idx)
2414 clear_tv(&argvars[idx]);
2415
2416 if (error != FCERR_NONE)
2417 {
2418 user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
2419 return FAIL;
2420 }
2421 *arg = argp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002422
2423 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002424}
2425
2426/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002427 * Evaluate what comes after a class:
2428 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002429 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002430 * - class constructor: SomeClass.new()
2431 * - object member: someObject.varname
2432 * - object method: someObject.SomeMethod()
2433 *
2434 * "*arg" points to the '.'.
2435 * "*arg" is advanced to after the member name or method call.
2436 *
2437 * Returns FAIL or OK.
2438 */
2439 int
2440class_object_index(
2441 char_u **arg,
2442 typval_T *rettv,
2443 evalarg_T *evalarg,
2444 int verbose UNUSED) // give error messages
2445{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002446 if (VIM_ISWHITE((*arg)[1]))
2447 {
2448 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
2449 return FAIL;
2450 }
2451
2452 ++*arg;
2453 char_u *name = *arg;
2454 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2455 if (name_end == name)
2456 return FAIL;
2457 size_t len = name_end - name;
2458
Ernie Raeld615a312023-10-05 20:28:16 +02002459 int did_emsg_save = did_emsg;
Bram Moolenaar552bdca2023-02-17 21:08:50 +00002460 class_T *cl;
2461 if (rettv->v_type == VAR_CLASS)
2462 cl = rettv->vval.v_class;
2463 else // VAR_OBJECT
2464 {
2465 if (rettv->vval.v_object == NULL)
2466 {
2467 emsg(_(e_using_null_object));
2468 return FAIL;
2469 }
2470 cl = rettv->vval.v_object->obj_class;
2471 }
2472
Bram Moolenaard13dd302023-03-11 20:56:35 +00002473 if (cl == NULL)
2474 {
2475 emsg(_(e_incomplete_type));
2476 return FAIL;
2477 }
2478
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002479 if (*name_end == '(')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002480 // Invoke the class or object method
2481 return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002482
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002483 else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002484 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002485 // Search in the object member variable table and the class member
2486 // variable table.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002487 int is_object = rettv->v_type == VAR_OBJECT;
2488 if (get_member_tv(cl, is_object, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002489 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002490 *arg = name_end;
2491 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002492 }
2493
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002494 // could be a class method or an object method
2495 int fidx;
2496 ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
2497 if (fp != NULL)
2498 {
2499 // Private methods are not accessible outside the class
2500 if (*name == '_')
2501 {
Ernie Rael03042a22023-11-11 08:53:32 +01002502 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002503 return FAIL;
2504 }
2505
2506 partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
2507 if (pt == NULL)
2508 return FAIL;
2509
2510 pt->pt_refcount = 1;
2511 if (is_object)
2512 {
2513 pt->pt_obj = rettv->vval.v_object;
2514 ++pt->pt_obj->obj_refcount;
2515 }
2516 pt->pt_auto = TRUE;
2517 pt->pt_func = fp;
2518 func_ptr_ref(pt->pt_func);
2519 rettv->v_type = VAR_PARTIAL;
2520 rettv->vval.v_partial = pt;
2521 *arg = name_end;
2522 return OK;
2523 }
2524
Ernie Raeld615a312023-10-05 20:28:16 +02002525 if (did_emsg == did_emsg_save)
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +02002526 member_not_found_msg(cl, rettv->v_type, name, len);
Bram Moolenaard505d172022-12-18 21:42:55 +00002527 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002528
2529 return FAIL;
2530}
2531
2532/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002533 * If "arg" points to a class or object method, return it.
2534 * Otherwise return NULL.
2535 */
2536 ufunc_T *
2537find_class_func(char_u **arg)
2538{
2539 char_u *name = *arg;
2540 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2541 if (name_end == name || *name_end != '.')
2542 return NULL;
2543
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002544 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002545 size_t len = name_end - name;
2546 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002547 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00002548 if (eval_variable(name, (int)len,
2549 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002550 return NULL;
2551 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00002552 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002553
2554 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
2555 : tv.vval.v_object->obj_class;
2556 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002557 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002558 char_u *fname = name_end + 1;
2559 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
2560 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00002561 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002562 len = fname_end - fname;
2563
Ernie Rael4d00b832023-09-11 19:54:42 +02002564 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002565
Bram Moolenaareb533502022-12-14 15:06:11 +00002566fail_after_eval:
2567 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002568 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002569}
2570
2571/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002572 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002573 * Returns -1, if the variable is not found.
2574 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002575 */
2576 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002577class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002578{
Ernie Rael4d00b832023-09-11 19:54:42 +02002579 int idx;
2580 class_member_lookup(cl, name, namelen, &idx);
2581 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002582}
2583
2584/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002585 * Returns a pointer to the class member variable "name" in the class "cl".
2586 * Returns NULL if the variable is not found.
2587 * The member variable index is set in "idx".
2588 */
2589 ocmember_T *
2590class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2591{
Ernie Rael4d00b832023-09-11 19:54:42 +02002592 ocmember_T *ret_m = NULL;
2593 int ret_idx = -1;
2594 for (int i = 0; i < cl->class_class_member_count; ++i)
2595 {
2596 ocmember_T *m = &cl->class_class_members[i];
2597 if (namelen)
2598 {
2599 if (STRNCMP(name, m->ocm_name, namelen) == 0
2600 && m->ocm_name[namelen] == NUL)
2601 {
2602 ret_m = m;
2603 ret_idx = i;
2604 break;
2605 }
2606 }
2607 else if (STRCMP(name, m->ocm_name) == 0)
2608 {
2609 ret_m = m;
2610 ret_idx = i;
2611 break;
2612 }
2613 }
2614 if (idx != NULL)
2615 *idx = ret_idx;
2616 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002617}
2618
2619/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002620 * Returns a pointer to the class method "name" in class "cl".
2621 * Returns NULL if the method is not found.
2622 * The method index is set in "idx".
2623 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002624 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002625class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2626{
Ernie Rael4d00b832023-09-11 19:54:42 +02002627 ufunc_T *ret_fp = NULL;
2628 int ret_idx = -1;
2629 for (int i = 0; i < cl->class_class_function_count; ++i)
2630 {
2631 ufunc_T *fp = cl->class_class_functions[i];
2632 char_u *ufname = (char_u *)fp->uf_name;
2633 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2634 {
2635 ret_fp = fp;
2636 ret_idx = i;
2637 break;
2638 }
2639 }
2640 if (idx != NULL)
2641 *idx = ret_idx;
2642 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002643}
2644
2645/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002646 * Returns the index of class method "name" in the class "cl".
2647 * Returns -1, if the method is not found.
2648 */
2649 int
2650class_method_idx(class_T *cl, char_u *name, size_t namelen)
2651{
2652 int idx;
2653 class_method_lookup(cl, name, namelen, &idx);
2654 return idx;
2655}
2656
2657/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002658 * Returns the index of object member variable "name" in the class "cl".
2659 * Returns -1, if the variable is not found.
2660 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
2661 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002662 static int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002663object_member_idx(class_T *cl, char_u *name, size_t namelen)
2664{
Ernie Rael4d00b832023-09-11 19:54:42 +02002665 int idx;
2666 object_member_lookup(cl, name, namelen, &idx);
2667 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002668}
2669
2670/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002671 * Returns a pointer to the object member variable "name" in the class "cl".
2672 * Returns NULL if the variable is not found.
2673 * The object member variable index is set in "idx".
2674 */
2675 ocmember_T *
2676object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2677{
Ernie Rael4d00b832023-09-11 19:54:42 +02002678 ocmember_T *ret_m = NULL;
2679 int ret_idx = -1;
2680 for (int i = 0; i < cl->class_obj_member_count; ++i)
2681 {
2682 ocmember_T *m = &cl->class_obj_members[i];
2683 if (namelen)
2684 {
2685 if (STRNCMP(name, m->ocm_name, namelen) == 0
2686 && m->ocm_name[namelen] == NUL)
2687 {
2688 ret_m = m;
2689 ret_idx = i;
2690 break;
2691 }
2692 }
2693 else if (STRCMP(name, m->ocm_name) == 0)
2694 {
2695 ret_m = m;
2696 ret_idx = i;
2697 break;
2698 }
2699 }
2700 if (idx != NULL)
2701 *idx = ret_idx;
2702 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002703}
2704
2705/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002706 * Returns a pointer to the object method "name" in class "cl".
2707 * Returns NULL if the method is not found.
2708 * The object method index is set in "idx".
2709 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002710 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002711object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2712{
Ernie Rael4d00b832023-09-11 19:54:42 +02002713 ufunc_T *ret_fp = NULL;
2714 int ret_idx = -1;
2715 for (int i = 0; i < cl->class_obj_method_count; ++i)
2716 {
2717 ufunc_T *fp = cl->class_obj_methods[i];
2718 // Use a separate pointer to avoid that ASAN complains about
2719 // uf_name[] only being 4 characters.
2720 char_u *ufname = (char_u *)fp->uf_name;
2721 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2722 {
2723 ret_fp = fp;
2724 ret_idx = i;
2725 break;
2726 }
2727 }
2728 if (idx != NULL)
2729 *idx = ret_idx;
2730 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002731}
2732
2733/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002734 * Returns the index of object method "name" in the class "cl".
2735 * Returns -1, if the method is not found.
2736 */
2737 int
2738object_method_idx(class_T *cl, char_u *name, size_t namelen)
2739{
2740 int idx;
2741 object_method_lookup(cl, name, namelen, &idx);
2742 return idx;
2743}
2744
2745/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002746 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
2747 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
2748 * object member variable.
2749 *
2750 * Returns a pointer to the member variable structure if variable is found.
2751 * Otherwise returns NULL. The member variable index is set in "*idx".
2752 */
2753 ocmember_T *
2754member_lookup(
2755 class_T *cl,
2756 vartype_T v_type,
2757 char_u *name,
2758 size_t namelen,
2759 int *idx)
2760{
2761 if (v_type == VAR_CLASS)
2762 return class_member_lookup(cl, name, namelen, idx);
2763 else
2764 return object_member_lookup(cl, name, namelen, idx);
2765}
2766
2767/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02002768 * Find the class that defines the named member. Look up the hierarchy
2769 * starting at "cl".
2770 *
2771 * Return the class that defines the member "name", else NULL.
2772 * Fill in "p_m", if specified, for ocmember_T in found class.
2773 */
2774// NOTE: if useful for something could also indirectly return vartype and idx.
2775 static class_T *
2776class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m)
2777{
2778 class_T *cl_found = NULL;
2779 vartype_T vartype = VAR_UNKNOWN;
2780 ocmember_T *m_found = NULL;
2781
2782 len = len != 0 ? len : STRLEN(name);
2783
2784 // Loop assumes if member is not defined in "cl", then it is not
2785 // defined in any super class; the last class where it's found is the
2786 // class where it is defined. Once the vartype is found, the other
2787 // type is no longer checked.
2788 for (class_T *super = cl; super != NULL; super = super->class_extends)
2789 {
2790 class_T *cl_tmp = NULL;
2791 ocmember_T *m = NULL;
2792 if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT)
2793 {
2794 if ((m = object_member_lookup(super, name, len, NULL)) != NULL)
2795 {
2796 cl_tmp = super;
2797 vartype = VAR_OBJECT;
2798 }
2799 }
2800 if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS)
2801 {
2802 if (( m = class_member_lookup(super, name, len, NULL)) != NULL)
2803 {
2804 cl_tmp = super;
2805 vartype = VAR_OBJECT;
2806 }
2807 }
2808 if (cl_tmp == NULL)
2809 break; // member is not in this or any super class.
2810 cl_found = cl_tmp;
2811 m_found = m;
2812 }
2813 if (p_m != NULL)
2814 *p_m = m_found;
2815 return cl_found;
2816}
2817
2818/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002819 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
2820 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
2821 *
2822 * Returns a pointer to the method structure if variable is found.
2823 * Otherwise returns NULL. The method variable index is set in "*idx".
2824 */
2825 ufunc_T *
2826method_lookup(
2827 class_T *cl,
2828 vartype_T v_type,
2829 char_u *name,
2830 size_t namelen,
2831 int *idx)
2832{
2833 if (v_type == VAR_CLASS)
2834 return class_method_lookup(cl, name, namelen, idx);
2835 else
2836 return object_method_lookup(cl, name, namelen, idx);
2837}
2838
2839/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00002840 * Return TRUE if current context "cctx_arg" is inside class "cl".
2841 * Return FALSE if not.
2842 */
2843 int
2844inside_class(cctx_T *cctx_arg, class_T *cl)
2845{
2846 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002847 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002848 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002849 return TRUE;
2850 return FALSE;
2851}
2852
2853/*
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002854 * Return TRUE if object/class variable "m" is read-only.
2855 * Also give an error message.
2856 */
2857 int
2858oc_var_check_ro(class_T *cl, ocmember_T *m)
2859{
2860 if (m->ocm_flags & (OCMFLAG_FINAL | OCMFLAG_CONST))
2861 {
2862 semsg(_(e_cannot_change_readonly_variable_str_in_class_str),
2863 m->ocm_name, cl->class_name);
2864 return TRUE;
2865 }
2866 return FALSE;
2867}
2868
2869/*
2870 * Lock all the constant object variables. Called after creating and
2871 * initializing a new object.
2872 */
2873 void
2874obj_lock_const_vars(object_T *obj)
2875{
2876 for (int i = 0; i < obj->obj_class->class_obj_member_count; i++)
2877 {
2878 ocmember_T *ocm = &obj->obj_class->class_obj_members[i];
2879 if (ocm->ocm_flags & OCMFLAG_CONST)
2880 {
2881 typval_T *mtv = ((typval_T *)(obj + 1)) + i;
2882 item_lock(mtv, DICT_MAXNEST, TRUE, TRUE);
2883 }
2884 }
2885}
2886
2887/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002888 * Make a copy of an object.
2889 */
2890 void
2891copy_object(typval_T *from, typval_T *to)
2892{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002893 if (from->vval.v_object == NULL)
2894 to->vval.v_object = NULL;
2895 else
2896 {
2897 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002898 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002899 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002900}
2901
2902/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002903 * Make a copy of a class.
2904 */
2905 void
2906copy_class(typval_T *from, typval_T *to)
2907{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002908 if (from->vval.v_class == NULL)
2909 to->vval.v_class = NULL;
2910 else
2911 {
2912 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002913 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002914 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002915}
2916
2917/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002918 * Free the class "cl" and its contents.
2919 */
2920 static void
2921class_free(class_T *cl)
2922{
2923 // Freeing what the class contains may recursively come back here.
2924 // Clear "class_name" first, if it is NULL the class does not need to
2925 // be freed.
2926 VIM_CLEAR(cl->class_name);
2927
2928 class_unref(cl->class_extends);
2929
2930 for (int i = 0; i < cl->class_interface_count; ++i)
2931 {
2932 vim_free(((char_u **)cl->class_interfaces)[i]);
2933 if (cl->class_interfaces_cl[i] != NULL)
2934 class_unref(cl->class_interfaces_cl[i]);
2935 }
2936 vim_free(cl->class_interfaces);
2937 vim_free(cl->class_interfaces_cl);
2938
2939 itf2class_T *next;
2940 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2941 {
2942 next = i2c->i2c_next;
2943 vim_free(i2c);
2944 }
2945
2946 for (int i = 0; i < cl->class_class_member_count; ++i)
2947 {
2948 ocmember_T *m = &cl->class_class_members[i];
2949 vim_free(m->ocm_name);
2950 vim_free(m->ocm_init);
2951 if (cl->class_members_tv != NULL)
2952 clear_tv(&cl->class_members_tv[i]);
2953 }
2954 vim_free(cl->class_class_members);
2955 vim_free(cl->class_members_tv);
2956
2957 for (int i = 0; i < cl->class_obj_member_count; ++i)
2958 {
2959 ocmember_T *m = &cl->class_obj_members[i];
2960 vim_free(m->ocm_name);
2961 vim_free(m->ocm_init);
2962 }
2963 vim_free(cl->class_obj_members);
2964
2965 for (int i = 0; i < cl->class_class_function_count; ++i)
2966 {
2967 ufunc_T *uf = cl->class_class_functions[i];
2968 func_clear_free(uf, FALSE);
2969 }
2970 vim_free(cl->class_class_functions);
2971
2972 for (int i = 0; i < cl->class_obj_method_count; ++i)
2973 {
2974 ufunc_T *uf = cl->class_obj_methods[i];
2975 func_clear_free(uf, FALSE);
2976 }
2977 vim_free(cl->class_obj_methods);
2978
2979 clear_type_list(&cl->class_type_list);
2980
2981 class_cleared(cl);
2982
2983 vim_free(cl);
2984}
2985
2986/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002987 * Unreference a class. Free it when the reference count goes down to zero.
2988 */
2989 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002990class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002991{
Bram Moolenaard505d172022-12-18 21:42:55 +00002992 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002993 class_free(cl);
2994}
2995
2996/*
2997 * Go through the list of all classes and free items without "copyID".
2998 */
2999 int
3000class_free_nonref(int copyID)
3001{
3002 int did_free = FALSE;
3003
3004 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003005 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003006 next_nonref_class = cl->class_next_used;
3007 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003008 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003009 // Free the class and items it contains.
3010 class_free(cl);
3011 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003012 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003013 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003014
3015 next_nonref_class = NULL;
3016 return did_free;
3017}
3018
3019 int
3020set_ref_in_classes(int copyID)
3021{
3022 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3023 set_ref_in_item_class(cl, copyID, NULL, NULL);
3024
3025 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003026}
3027
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003028static object_T *first_object = NULL;
3029
3030/*
3031 * Call this function when an object has been created. It will be added to the
3032 * list headed by "first_object".
3033 */
3034 void
3035object_created(object_T *obj)
3036{
3037 if (first_object != NULL)
3038 {
3039 obj->obj_next_used = first_object;
3040 first_object->obj_prev_used = obj;
3041 }
3042 first_object = obj;
3043}
3044
3045/*
3046 * Call this function when an object has been cleared and is about to be freed.
3047 * It is removed from the list headed by "first_object".
3048 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003049 static void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003050object_cleared(object_T *obj)
3051{
3052 if (obj->obj_next_used != NULL)
3053 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
3054 if (obj->obj_prev_used != NULL)
3055 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
3056 else if (first_object == obj)
3057 first_object = obj->obj_next_used;
3058}
3059
3060/*
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003061 * Free the contents of an object ignoring the reference count.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003062 */
3063 static void
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003064object_free_contents(object_T *obj)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003065{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003066 class_T *cl = obj->obj_class;
3067
3068 if (!cl)
3069 return;
3070
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003071 // Avoid a recursive call, it can happen if "obj" has a circular reference.
3072 obj->obj_refcount = INT_MAX;
3073
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003074 // the member values are just after the object structure
3075 typval_T *tv = (typval_T *)(obj + 1);
3076 for (int i = 0; i < cl->class_obj_member_count; ++i)
3077 clear_tv(tv + i);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003078}
3079
3080 static void
3081object_free_object(object_T *obj)
3082{
3083 class_T *cl = obj->obj_class;
3084
3085 if (!cl)
3086 return;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003087
3088 // Remove from the list headed by "first_object".
3089 object_cleared(obj);
3090
3091 vim_free(obj);
3092 class_unref(cl);
3093}
3094
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003095 static void
3096object_free(object_T *obj)
3097{
3098 if (in_free_unref_items)
3099 return;
3100
3101 object_free_contents(obj);
3102 object_free_object(obj);
3103}
3104
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003105/*
3106 * Unreference an object.
3107 */
3108 void
3109object_unref(object_T *obj)
3110{
3111 if (obj != NULL && --obj->obj_refcount <= 0)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003112 object_free(obj);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003113}
3114
3115/*
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003116 * Go through the list of all objects and free items without "copyID".
3117 */
3118 int
3119object_free_nonref(int copyID)
3120{
3121 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003122
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003123 for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003124 {
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003125 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3126 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003127 // Free the object contents. Object itself will be freed later.
3128 object_free_contents(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003129 did_free = TRUE;
3130 }
3131 }
3132
3133 return did_free;
3134}
3135
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003136 void
3137object_free_items(int copyID)
3138{
3139 object_T *obj_next;
3140
3141 for (object_T *obj = first_object; obj != NULL; obj = obj_next)
3142 {
3143 obj_next = obj->obj_next_used;
3144 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3145 object_free_object(obj);
3146 }
3147}
3148
LemonBoyafe04662023-08-23 21:08:11 +02003149/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003150 * Output message which takes a variable name and the class that defines it.
3151 * "cl" is that class where the name was found. Search "cl"'s hierarchy to
3152 * find the defining class.
3153 */
3154 void
3155emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl)
3156{
3157 ocmember_T *m;
3158 class_T *cl_def = class_defining_member(cl, name, len, &m);
3159 if (cl_def != NULL)
3160 semsg(_(msg), m->ocm_name, cl_def->class_name);
3161 else
3162 emsg(_(e_internal_error_please_report_a_bug));
3163}
3164
3165/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003166 * Echo a class or object method not found message.
3167 */
3168 void
3169method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3170{
3171 char_u *method_name = vim_strnsave(name, len);
3172 if ((v_type == VAR_OBJECT)
3173 && (class_method_idx(cl, name, len) >= 0))
3174 {
3175 // If this is a class method, then give a different error
3176 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003177 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003178 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003179 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003180 method_name, cl->class_name);
3181 }
3182 else if ((v_type == VAR_CLASS)
3183 && (object_method_idx(cl, name, len) >= 0))
3184 {
3185 // If this is an object method, then give a different error
3186 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003187 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003188 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003189 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003190 method_name, cl->class_name);
3191 }
3192 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003193 semsg(_(e_method_not_found_on_class_str_str), method_name,
3194 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003195 vim_free(method_name);
3196}
3197
3198/*
3199 * Echo a class or object member not found message.
3200 */
3201 void
3202member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3203{
3204 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
3205
3206 if (v_type == VAR_OBJECT)
3207 {
3208 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003209 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003210 varname, cl->class_name);
3211 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003212 semsg(_(e_variable_not_found_on_object_str_str), varname,
3213 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003214 }
3215 else
3216 {
3217 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003218 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003219 varname, cl->class_name);
3220 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003221 semsg(_(e_class_variable_str_not_found_in_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003222 varname, cl->class_name);
3223 }
3224 vim_free(varname);
3225}
3226
3227/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02003228 * Return TRUE when the class "cl", its base class or one of the implemented
3229 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02003230 */
3231 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003232class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02003233{
3234 if (cl == other_cl)
3235 return TRUE;
3236
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003237 // Recursively check the base classes.
3238 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02003239 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003240 if (cl == other_cl)
3241 return TRUE;
3242 // Check the implemented interfaces and the super interfaces
3243 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003244 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003245 class_T *intf = cl->class_interfaces_cl[i];
3246 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003247 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003248 if (intf == other_cl)
3249 return TRUE;
3250 // check the super interfaces
3251 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003252 }
3253 }
LemonBoyafe04662023-08-23 21:08:11 +02003254 }
3255
3256 return FALSE;
3257}
3258
3259/*
Ernie Rael2025af12023-12-12 16:58:00 +01003260 * "instanceof(object, classinfo, ...)" function
LemonBoyafe04662023-08-23 21:08:11 +02003261 */
3262 void
3263f_instanceof(typval_T *argvars, typval_T *rettv)
3264{
3265 typval_T *object_tv = &argvars[0];
3266 typval_T *classinfo_tv = &argvars[1];
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02003267 class_T *c;
LemonBoyafe04662023-08-23 21:08:11 +02003268
3269 rettv->vval.v_number = VVAL_FALSE;
3270
3271 if (check_for_object_arg(argvars, 0) == FAIL
Ernie Rael2025af12023-12-12 16:58:00 +01003272 || check_for_class_or_typealias_args(argvars, 1) == FAIL)
LemonBoyafe04662023-08-23 21:08:11 +02003273 return;
3274
Ernie Rael3da696d2023-09-19 20:14:18 +02003275 if (object_tv->vval.v_object == NULL)
3276 return;
3277
Ernie Rael2025af12023-12-12 16:58:00 +01003278 for (; classinfo_tv->v_type != VAR_UNKNOWN; ++classinfo_tv)
LemonBoyafe04662023-08-23 21:08:11 +02003279 {
Ernie Rael2025af12023-12-12 16:58:00 +01003280 if (classinfo_tv->v_type == VAR_TYPEALIAS)
3281 c = classinfo_tv->vval.v_typealias->ta_type->tt_class;
3282 else
3283 c = classinfo_tv->vval.v_class;
3284
3285 if (class_instance_of(object_tv->vval.v_object->obj_class, c))
LemonBoyafe04662023-08-23 21:08:11 +02003286 {
Ernie Rael2025af12023-12-12 16:58:00 +01003287 rettv->vval.v_number = VVAL_TRUE;
3288 return;
LemonBoyafe04662023-08-23 21:08:11 +02003289 }
3290 }
LemonBoyafe04662023-08-23 21:08:11 +02003291}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00003292
3293#endif // FEAT_EVAL