blob: ec1bb86d09d79a57690924f1168a76d57a700057 [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
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001777 CLEAR_FIELD(ea);
1778 ea.cmd = line;
1779 ea.arg = p;
1780 ea.cmdidx = CMD_def;
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001781 ea.ea_getline = eap->ea_getline;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001782 ea.cookie = eap->cookie;
1783
1784 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001785 int class_flags;
1786 if (is_class)
1787 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
1788 else
1789 class_flags = CF_INTERFACE;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001790 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02001791 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001792 ga_clear_strings(&lines_to_free);
1793
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001794 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001795 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001796 char_u *name = uf->uf_name;
1797 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001798
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001799 if (!is_class && *name == '_')
1800 {
1801 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01001802 semsg(_(e_protected_method_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001803 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001804 func_clear_free(uf, FALSE);
1805 break;
1806 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001807 if (is_new && !is_valid_constructor(uf, is_abstract,
1808 has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001809 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001810 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001811 break;
1812 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001813
Bram Moolenaar58b40092023-01-11 15:59:05 +00001814 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001815 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001816 {
1817 success = FALSE;
1818 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001819 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001820 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001821
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001822 garray_T *fgap = has_static || is_new
1823 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001824 if (ga_grow(fgap, 1) == OK)
1825 {
1826 if (is_new)
1827 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001828
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001829 if (abstract_method)
1830 uf->uf_flags |= FC_ABSTRACT;
1831
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001832 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1833 ++fgap->ga_len;
1834 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001835 }
1836 }
1837
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001838 else
1839 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001840 if (is_class)
1841 semsg(_(e_not_valid_command_in_class_str), line);
1842 else
1843 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001844 break;
1845 }
1846 }
1847 vim_free(theline);
1848
Bram Moolenaar83677162023-01-08 19:54:10 +00001849 class_T *extends_cl = NULL; // class from "extends" argument
1850
1851 /*
1852 * Check a few things before defining the class.
1853 */
1854
1855 // Check the "extends" class is valid.
1856 if (success && extends != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001857 success = validate_extends_class(extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00001858 VIM_CLEAR(extends);
1859
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001860 // Check the new object methods to make sure their access (public or
1861 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001862 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001863 success = validate_extends_methods(&objmethods, extends_cl);
1864
1865 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001866 // variables in the extended class lineage. If an interface is extending
1867 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001868 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001869 {
1870 if (is_class)
1871 success = extends_check_dup_members(&objmembers, extends_cl);
1872 else
1873 success = extends_check_intf_var_type(&objmembers, extends_cl);
1874 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001875
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001876 // When extending an abstract class, make sure all the abstract methods in
1877 // the parent class are implemented. If the current class is an abstract
1878 // class, then there is no need for this check.
1879 if (success && !is_abstract && extends_cl != NULL
1880 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001881 success = validate_abstract_class_methods(&classfunctions,
1882 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001883
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001884 class_T **intf_classes = NULL;
1885
Bram Moolenaar83677162023-01-08 19:54:10 +00001886 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001887 if (success && ga_impl.ga_len > 0)
1888 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001889 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1890
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001891 success = validate_implements_classes(&ga_impl, intf_classes,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02001892 &objmethods, &objmembers, extends_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001893 }
1894
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001895 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001896 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001897 success = check_func_arg_names(&classfunctions, &objmethods,
1898 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001899
Bram Moolenaareb533502022-12-14 15:06:11 +00001900 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001901 if (success)
1902 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001903 // "endclass" encountered without failures: Create the class.
1904
Bram Moolenaareb533502022-12-14 15:06:11 +00001905 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001906 if (cl == NULL)
1907 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001908 if (!is_class)
1909 cl->class_flags = CLASS_INTERFACE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001910 else if (is_abstract)
1911 cl->class_flags = CLASS_ABSTRACT;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001912
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001913 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001914 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001915 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001916 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001917
Bram Moolenaard0200c82023-01-28 15:19:40 +00001918 if (extends_cl != NULL)
1919 {
1920 cl->class_extends = extends_cl;
1921 extends_cl->class_flags |= CLASS_EXTENDED;
1922 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001923
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001924 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00001925 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001926 NULL,
1927 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00001928 &cl->class_class_members,
1929 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001930 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001931 extends_cl == NULL ? NULL
1932 : extends_cl->class_obj_members,
1933 extends_cl == NULL ? 0
1934 : extends_cl->class_obj_member_count,
1935 &cl->class_obj_members,
1936 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001937 goto cleanup;
1938
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001939 if (ga_impl.ga_len > 0)
1940 {
1941 // Move the "implements" names into the class.
1942 cl->class_interface_count = ga_impl.ga_len;
1943 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1944 if (cl->class_interfaces == NULL)
1945 goto cleanup;
1946 for (int i = 0; i < ga_impl.ga_len; ++i)
1947 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1948 VIM_CLEAR(ga_impl.ga_data);
1949 ga_impl.ga_len = 0;
1950
Bram Moolenaard0200c82023-01-28 15:19:40 +00001951 cl->class_interfaces_cl = intf_classes;
1952 intf_classes = NULL;
1953 }
1954
1955 if (cl->class_interface_count > 0 || extends_cl != NULL)
1956 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001957 // Add a method and member lookup table to each of the interface
1958 // classes.
1959 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1960 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001961 }
1962
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001963 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001964 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001965 add_class_members(cl, eap, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001966
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001967 int have_new = FALSE;
1968 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001969 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001970 {
1971 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1972 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001973 {
1974 have_new = TRUE;
1975 break;
1976 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001977 }
1978
1979 if (have_new)
1980 // The return type of new() is an object of class "cl"
1981 class_func->uf_ret_type->tt_class = cl;
1982 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001983 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001984 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001985
Bram Moolenaar58b40092023-01-11 15:59:05 +00001986 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001987 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1988 &objmethods) == FAIL)
1989 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001990
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001991 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001992 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001993 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001994 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001995 cl->class_type_list = type_list;
1996
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001997 class_created(cl);
1998
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001999 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00002000 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002001
2002 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00002003 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02002004 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002005 typval_T tv;
2006 tv.v_type = VAR_CLASS;
2007 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00002008 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00002009 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002010 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00002011 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002012 return;
2013 }
2014
2015cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00002016 if (cl != NULL)
2017 {
2018 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002019 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002020 if (cl->class_interfaces != NULL)
2021 {
2022 for (int i = 0; i < cl->class_interface_count; ++i)
2023 vim_free(cl->class_interfaces[i]);
2024 vim_free(cl->class_interfaces);
2025 }
2026 if (cl->class_interfaces_cl != NULL)
2027 {
2028 for (int i = 0; i < cl->class_interface_count; ++i)
2029 class_unref(cl->class_interfaces_cl[i]);
2030 vim_free(cl->class_interfaces_cl);
2031 }
Bram Moolenaareb533502022-12-14 15:06:11 +00002032 vim_free(cl->class_obj_members);
2033 vim_free(cl->class_obj_methods);
2034 vim_free(cl);
2035 }
2036
Bram Moolenaar83677162023-01-08 19:54:10 +00002037 vim_free(extends);
2038 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002039
2040 if (intf_classes != NULL)
2041 {
2042 for (int i = 0; i < ga_impl.ga_len; ++i)
2043 class_unref(intf_classes[i]);
2044 vim_free(intf_classes);
2045 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00002046 ga_clear_strings(&ga_impl);
2047
Bram Moolenaard505d172022-12-18 21:42:55 +00002048 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002049 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002050 garray_T *gap = round == 1 ? &classmembers : &objmembers;
2051 if (gap->ga_len == 0 || gap->ga_data == NULL)
2052 continue;
2053
2054 for (int i = 0; i < gap->ga_len; ++i)
2055 {
2056 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2057 vim_free(m->ocm_name);
2058 vim_free(m->ocm_init);
2059 }
2060 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002061 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002062
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002063 for (int i = 0; i < objmethods.ga_len; ++i)
2064 {
2065 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2066 func_clear_free(uf, FALSE);
2067 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002068 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002069
2070 for (int i = 0; i < classfunctions.ga_len; ++i)
2071 {
2072 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2073 func_clear_free(uf, FALSE);
2074 }
2075 ga_clear(&classfunctions);
2076
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002077 clear_type_list(&type_list);
2078}
2079
2080/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002081 * Find member "name" in class "cl", set "member_idx" to the member index and
2082 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002083 * When "is_object" is TRUE, then look for object members. Otherwise look for
2084 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002085 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002086 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002087 */
2088 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002089oc_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002090 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002091 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002092 char_u *name,
2093 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002094 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002095{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002096 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002097 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002098
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002099 *member_idx = -1; // not found (yet)
2100
2101 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2102 member_idx);
2103 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002104 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002105 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2106 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002107 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002108 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002109
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002110 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002111}
2112
2113/*
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002114 * Given a class or object variable index, return the variable type
2115 */
2116 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002117oc_member_type_by_idx(
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002118 class_T *cl,
2119 int is_object,
2120 int member_idx)
2121{
2122 ocmember_T *m;
2123 int member_count;
2124
2125 if (is_object)
2126 {
2127 m = cl->class_obj_members;
2128 member_count = cl->class_obj_member_count;
2129 }
2130 else
2131 {
2132 m = cl->class_class_members;
2133 member_count = cl->class_class_member_count;
2134 }
2135
2136 if (member_idx >= member_count)
2137 return NULL;
2138
2139 return m[member_idx].ocm_type;
2140}
2141
2142/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002143 * Handle ":enum" up to ":endenum".
2144 */
2145 void
2146ex_enum(exarg_T *eap UNUSED)
2147{
2148 // TODO
2149}
2150
2151/*
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002152 * Type aliases (:type)
2153 */
2154
2155 void
2156typealias_free(typealias_T *ta)
2157{
2158 // ta->ta_type is freed in clear_type_list()
2159 vim_free(ta->ta_name);
2160 vim_free(ta);
2161}
2162
2163 void
2164typealias_unref(typealias_T *ta)
2165{
2166 if (ta != NULL && --ta->ta_refcount <= 0)
2167 typealias_free(ta);
2168}
2169
2170/*
2171 * Handle ":type". Create an alias for a type specification.
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002172 */
2173 void
2174ex_type(exarg_T *eap UNUSED)
2175{
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002176 char_u *arg = eap->arg;
2177
2178 if (!current_script_is_vim9()
2179 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002180 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002181 {
2182 emsg(_(e_type_can_only_be_defined_in_vim9_script));
2183 return;
2184 }
2185
2186 if (*arg == NUL)
2187 {
2188 emsg(_(e_missing_typealias_name));
2189 return;
2190 }
2191
2192 if (!ASCII_ISUPPER(*arg))
2193 {
2194 semsg(_(e_type_name_must_start_with_uppercase_letter_str), arg);
2195 return;
2196 }
2197
2198 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
2199 if (!IS_WHITE_OR_NUL(*name_end))
2200 {
2201 semsg(_(e_white_space_required_after_name_str), arg);
2202 return;
2203 }
2204 char_u *name_start = arg;
2205
2206 arg = skipwhite(name_end);
2207 if (*arg != '=')
2208 {
2209 semsg(_(e_missing_equal_str), arg);
2210 return;
2211 }
2212 if (!IS_WHITE_OR_NUL(*(arg + 1)))
2213 {
2214 semsg(_(e_white_space_required_after_str_str), "=", arg);
2215 return;
2216 }
2217 arg++;
2218 arg = skipwhite(arg);
2219
2220 if (*arg == NUL)
2221 {
2222 emsg(_(e_missing_typealias_type));
2223 return;
2224 }
2225
2226 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
2227 type_T *type = parse_type(&arg, &si->sn_type_list, TRUE);
2228 if (type == NULL)
2229 return;
2230
2231 if (*arg != NUL)
2232 {
2233 // some text after the type
2234 semsg(_(e_trailing_characters_str), arg);
2235 return;
2236 }
2237
2238 int cc = *name_end;
2239 *name_end = NUL;
2240
2241 typval_T tv;
2242 tv.v_type = VAR_UNKNOWN;
2243 if (eval_variable_import(name_start, &tv) == OK)
2244 {
2245 if (tv.v_type == VAR_TYPEALIAS)
2246 semsg(_(e_typealias_already_exists_for_str), name_start);
2247 else
2248 semsg(_(e_redefining_script_item_str), name_start);
2249 clear_tv(&tv);
2250 goto done;
2251 }
2252
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02002253 // Create a script-local variable for the type alias.
2254 if (type->tt_type != VAR_OBJECT)
2255 {
2256 tv.v_type = VAR_TYPEALIAS;
2257 tv.v_lock = 0;
2258 tv.vval.v_typealias = ALLOC_CLEAR_ONE(typealias_T);
2259 ++tv.vval.v_typealias->ta_refcount;
2260 tv.vval.v_typealias->ta_name = vim_strsave(name_start);
2261 tv.vval.v_typealias->ta_type = type;
2262 }
2263 else
2264 {
2265 // When creating a type alias for a class, use the class type itself to
2266 // create the type alias variable. This is needed to use the type
2267 // alias to invoke class methods (e.g. new()) and use class variables.
2268 tv.v_type = VAR_CLASS;
2269 tv.v_lock = 0;
2270 tv.vval.v_class = type->tt_class;
2271 ++tv.vval.v_class->class_refcount;
2272 }
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002273 set_var_const(name_start, current_sctx.sc_sid, NULL, &tv, FALSE,
2274 ASSIGN_CONST | ASSIGN_FINAL, 0);
2275
2276done:
2277 *name_end = cc;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002278}
2279
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002280/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002281 * Returns OK if a member variable named "name" is present in the class "cl".
2282 * Otherwise returns FAIL. If found, the member variable typval is set in
2283 * "rettv". If "is_object" is TRUE, then the object member variable table is
2284 * searched. Otherwise the class member variable table is searched.
2285 */
2286 static int
2287get_member_tv(
2288 class_T *cl,
2289 int is_object,
2290 char_u *name,
2291 size_t namelen,
2292 typval_T *rettv)
2293{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002294 ocmember_T *m;
2295 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002296
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002297 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2298 &m_idx);
2299 if (m == NULL)
2300 return FAIL;
2301
2302 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002303 {
Ernie Rael03042a22023-11-11 08:53:32 +01002304 emsg_var_cl_define(e_cannot_access_protected_variable_str,
Ernie Raele6c9aa52023-10-06 19:55:52 +02002305 m->ocm_name, 0, cl);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002306 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002307 }
2308
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002309 if (is_object)
2310 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002311 // The object only contains a pointer to the class, the member values
2312 // array follows right after that.
2313 object_T *obj = rettv->vval.v_object;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002314 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2315 copy_tv(tv, rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002316 object_unref(obj);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002317 }
2318 else
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002319 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002320 copy_tv(&cl->class_members_tv[m_idx], rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002321 class_unref(cl);
2322 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002323
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002324 return OK;
2325}
2326
2327/*
2328 * Call an object or class method "name" in class "cl". The method return
2329 * value is returned in "rettv".
2330 */
2331 static int
2332call_oc_method(
2333 class_T *cl,
2334 char_u *name,
2335 size_t len,
2336 char_u *name_end,
2337 evalarg_T *evalarg,
2338 char_u **arg,
2339 typval_T *rettv)
2340{
2341 ufunc_T *fp;
2342 typval_T argvars[MAX_FUNC_ARGS + 1];
2343 int argcount = 0;
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002344 ocmember_T *ocm = NULL;
2345 int m_idx;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002346
2347 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
2348 if (fp == NULL)
2349 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002350 // could be an object or class funcref variable
2351 ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
2352 if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
2353 {
2354 method_not_found_msg(cl, rettv->v_type, name, len);
2355 return FAIL;
2356 }
2357
2358 if (rettv->v_type == VAR_OBJECT)
2359 {
2360 // funcref object variable
2361 object_T *obj = rettv->vval.v_object;
2362 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2363 copy_tv(tv, rettv);
2364 }
2365 else
2366 // funcref class variable
2367 copy_tv(&cl->class_members_tv[m_idx], rettv);
2368 *arg = name_end;
2369 return OK;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002370 }
2371
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002372 if (ocm == NULL && *fp->uf_name == '_')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002373 {
2374 // Cannot access a private method outside of a class
Ernie Rael03042a22023-11-11 08:53:32 +01002375 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002376 return FAIL;
2377 }
2378
2379 char_u *argp = name_end;
Ernie Raelb077b582023-12-14 20:11:44 +01002380 int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount, FALSE);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002381 if (ret == FAIL)
2382 return FAIL;
2383
2384 funcexe_T funcexe;
2385 CLEAR_FIELD(funcexe);
2386 funcexe.fe_evaluate = TRUE;
2387 if (rettv->v_type == VAR_OBJECT)
2388 {
2389 funcexe.fe_object = rettv->vval.v_object;
2390 ++funcexe.fe_object->obj_refcount;
2391 }
2392
2393 // Clear the class or object after calling the function, in
2394 // case the refcount is one.
2395 typval_T tv_tofree = *rettv;
2396 rettv->v_type = VAR_UNKNOWN;
2397
2398 // Call the user function. Result goes into rettv;
2399 int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
2400 NULL);
2401
2402 // Clear the previous rettv and the arguments.
2403 clear_tv(&tv_tofree);
2404 for (int idx = 0; idx < argcount; ++idx)
2405 clear_tv(&argvars[idx]);
2406
2407 if (error != FCERR_NONE)
2408 {
2409 user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
2410 return FAIL;
2411 }
2412 *arg = argp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002413
2414 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002415}
2416
2417/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002418 * Evaluate what comes after a class:
2419 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002420 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002421 * - class constructor: SomeClass.new()
2422 * - object member: someObject.varname
2423 * - object method: someObject.SomeMethod()
2424 *
2425 * "*arg" points to the '.'.
2426 * "*arg" is advanced to after the member name or method call.
2427 *
2428 * Returns FAIL or OK.
2429 */
2430 int
2431class_object_index(
2432 char_u **arg,
2433 typval_T *rettv,
2434 evalarg_T *evalarg,
2435 int verbose UNUSED) // give error messages
2436{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002437 if (VIM_ISWHITE((*arg)[1]))
2438 {
2439 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
2440 return FAIL;
2441 }
2442
2443 ++*arg;
2444 char_u *name = *arg;
2445 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2446 if (name_end == name)
2447 return FAIL;
2448 size_t len = name_end - name;
2449
Ernie Raeld615a312023-10-05 20:28:16 +02002450 int did_emsg_save = did_emsg;
Bram Moolenaar552bdca2023-02-17 21:08:50 +00002451 class_T *cl;
2452 if (rettv->v_type == VAR_CLASS)
2453 cl = rettv->vval.v_class;
2454 else // VAR_OBJECT
2455 {
2456 if (rettv->vval.v_object == NULL)
2457 {
2458 emsg(_(e_using_null_object));
2459 return FAIL;
2460 }
2461 cl = rettv->vval.v_object->obj_class;
2462 }
2463
Bram Moolenaard13dd302023-03-11 20:56:35 +00002464 if (cl == NULL)
2465 {
2466 emsg(_(e_incomplete_type));
2467 return FAIL;
2468 }
2469
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002470 if (*name_end == '(')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002471 // Invoke the class or object method
2472 return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002473
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002474 else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002475 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002476 // Search in the object member variable table and the class member
2477 // variable table.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002478 int is_object = rettv->v_type == VAR_OBJECT;
2479 if (get_member_tv(cl, is_object, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002480 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002481 *arg = name_end;
2482 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002483 }
2484
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002485 // could be a class method or an object method
2486 int fidx;
2487 ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
2488 if (fp != NULL)
2489 {
2490 // Private methods are not accessible outside the class
2491 if (*name == '_')
2492 {
Ernie Rael03042a22023-11-11 08:53:32 +01002493 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002494 return FAIL;
2495 }
2496
2497 partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
2498 if (pt == NULL)
2499 return FAIL;
2500
2501 pt->pt_refcount = 1;
2502 if (is_object)
2503 {
2504 pt->pt_obj = rettv->vval.v_object;
2505 ++pt->pt_obj->obj_refcount;
2506 }
2507 pt->pt_auto = TRUE;
2508 pt->pt_func = fp;
2509 func_ptr_ref(pt->pt_func);
2510 rettv->v_type = VAR_PARTIAL;
2511 rettv->vval.v_partial = pt;
2512 *arg = name_end;
2513 return OK;
2514 }
2515
Ernie Raeld615a312023-10-05 20:28:16 +02002516 if (did_emsg == did_emsg_save)
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +02002517 member_not_found_msg(cl, rettv->v_type, name, len);
Bram Moolenaard505d172022-12-18 21:42:55 +00002518 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002519
2520 return FAIL;
2521}
2522
2523/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002524 * If "arg" points to a class or object method, return it.
2525 * Otherwise return NULL.
2526 */
2527 ufunc_T *
2528find_class_func(char_u **arg)
2529{
2530 char_u *name = *arg;
2531 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2532 if (name_end == name || *name_end != '.')
2533 return NULL;
2534
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002535 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002536 size_t len = name_end - name;
2537 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002538 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00002539 if (eval_variable(name, (int)len,
2540 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002541 return NULL;
2542 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00002543 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002544
2545 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
2546 : tv.vval.v_object->obj_class;
2547 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002548 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002549 char_u *fname = name_end + 1;
2550 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
2551 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00002552 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002553 len = fname_end - fname;
2554
Ernie Rael4d00b832023-09-11 19:54:42 +02002555 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002556
Bram Moolenaareb533502022-12-14 15:06:11 +00002557fail_after_eval:
2558 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002559 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002560}
2561
2562/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002563 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002564 * Returns -1, if the variable is not found.
2565 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002566 */
2567 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002568class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002569{
Ernie Rael4d00b832023-09-11 19:54:42 +02002570 int idx;
2571 class_member_lookup(cl, name, namelen, &idx);
2572 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002573}
2574
2575/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002576 * Returns a pointer to the class member variable "name" in the class "cl".
2577 * Returns NULL if the variable is not found.
2578 * The member variable index is set in "idx".
2579 */
2580 ocmember_T *
2581class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2582{
Ernie Rael4d00b832023-09-11 19:54:42 +02002583 ocmember_T *ret_m = NULL;
2584 int ret_idx = -1;
2585 for (int i = 0; i < cl->class_class_member_count; ++i)
2586 {
2587 ocmember_T *m = &cl->class_class_members[i];
2588 if (namelen)
2589 {
2590 if (STRNCMP(name, m->ocm_name, namelen) == 0
2591 && m->ocm_name[namelen] == NUL)
2592 {
2593 ret_m = m;
2594 ret_idx = i;
2595 break;
2596 }
2597 }
2598 else if (STRCMP(name, m->ocm_name) == 0)
2599 {
2600 ret_m = m;
2601 ret_idx = i;
2602 break;
2603 }
2604 }
2605 if (idx != NULL)
2606 *idx = ret_idx;
2607 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002608}
2609
2610/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002611 * Returns a pointer to the class method "name" in class "cl".
2612 * Returns NULL if the method is not found.
2613 * The method index is set in "idx".
2614 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002615 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002616class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2617{
Ernie Rael4d00b832023-09-11 19:54:42 +02002618 ufunc_T *ret_fp = NULL;
2619 int ret_idx = -1;
2620 for (int i = 0; i < cl->class_class_function_count; ++i)
2621 {
2622 ufunc_T *fp = cl->class_class_functions[i];
2623 char_u *ufname = (char_u *)fp->uf_name;
2624 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2625 {
2626 ret_fp = fp;
2627 ret_idx = i;
2628 break;
2629 }
2630 }
2631 if (idx != NULL)
2632 *idx = ret_idx;
2633 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002634}
2635
2636/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002637 * Returns the index of class method "name" in the class "cl".
2638 * Returns -1, if the method is not found.
2639 */
2640 int
2641class_method_idx(class_T *cl, char_u *name, size_t namelen)
2642{
2643 int idx;
2644 class_method_lookup(cl, name, namelen, &idx);
2645 return idx;
2646}
2647
2648/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002649 * Returns the index of object member variable "name" in the class "cl".
2650 * Returns -1, if the variable is not found.
2651 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
2652 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002653 static int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002654object_member_idx(class_T *cl, char_u *name, size_t namelen)
2655{
Ernie Rael4d00b832023-09-11 19:54:42 +02002656 int idx;
2657 object_member_lookup(cl, name, namelen, &idx);
2658 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002659}
2660
2661/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002662 * Returns a pointer to the object member variable "name" in the class "cl".
2663 * Returns NULL if the variable is not found.
2664 * The object member variable index is set in "idx".
2665 */
2666 ocmember_T *
2667object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2668{
Ernie Rael4d00b832023-09-11 19:54:42 +02002669 ocmember_T *ret_m = NULL;
2670 int ret_idx = -1;
2671 for (int i = 0; i < cl->class_obj_member_count; ++i)
2672 {
2673 ocmember_T *m = &cl->class_obj_members[i];
2674 if (namelen)
2675 {
2676 if (STRNCMP(name, m->ocm_name, namelen) == 0
2677 && m->ocm_name[namelen] == NUL)
2678 {
2679 ret_m = m;
2680 ret_idx = i;
2681 break;
2682 }
2683 }
2684 else if (STRCMP(name, m->ocm_name) == 0)
2685 {
2686 ret_m = m;
2687 ret_idx = i;
2688 break;
2689 }
2690 }
2691 if (idx != NULL)
2692 *idx = ret_idx;
2693 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002694}
2695
2696/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002697 * Returns a pointer to the object method "name" in class "cl".
2698 * Returns NULL if the method is not found.
2699 * The object method index is set in "idx".
2700 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002701 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002702object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2703{
Ernie Rael4d00b832023-09-11 19:54:42 +02002704 ufunc_T *ret_fp = NULL;
2705 int ret_idx = -1;
2706 for (int i = 0; i < cl->class_obj_method_count; ++i)
2707 {
2708 ufunc_T *fp = cl->class_obj_methods[i];
2709 // Use a separate pointer to avoid that ASAN complains about
2710 // uf_name[] only being 4 characters.
2711 char_u *ufname = (char_u *)fp->uf_name;
2712 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2713 {
2714 ret_fp = fp;
2715 ret_idx = i;
2716 break;
2717 }
2718 }
2719 if (idx != NULL)
2720 *idx = ret_idx;
2721 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002722}
2723
2724/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002725 * Returns the index of object method "name" in the class "cl".
2726 * Returns -1, if the method is not found.
2727 */
2728 int
2729object_method_idx(class_T *cl, char_u *name, size_t namelen)
2730{
2731 int idx;
2732 object_method_lookup(cl, name, namelen, &idx);
2733 return idx;
2734}
2735
2736/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002737 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
2738 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
2739 * object member variable.
2740 *
2741 * Returns a pointer to the member variable structure if variable is found.
2742 * Otherwise returns NULL. The member variable index is set in "*idx".
2743 */
2744 ocmember_T *
2745member_lookup(
2746 class_T *cl,
2747 vartype_T v_type,
2748 char_u *name,
2749 size_t namelen,
2750 int *idx)
2751{
2752 if (v_type == VAR_CLASS)
2753 return class_member_lookup(cl, name, namelen, idx);
2754 else
2755 return object_member_lookup(cl, name, namelen, idx);
2756}
2757
2758/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02002759 * Find the class that defines the named member. Look up the hierarchy
2760 * starting at "cl".
2761 *
2762 * Return the class that defines the member "name", else NULL.
2763 * Fill in "p_m", if specified, for ocmember_T in found class.
2764 */
2765// NOTE: if useful for something could also indirectly return vartype and idx.
2766 static class_T *
2767class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m)
2768{
2769 class_T *cl_found = NULL;
2770 vartype_T vartype = VAR_UNKNOWN;
2771 ocmember_T *m_found = NULL;
2772
2773 len = len != 0 ? len : STRLEN(name);
2774
2775 // Loop assumes if member is not defined in "cl", then it is not
2776 // defined in any super class; the last class where it's found is the
2777 // class where it is defined. Once the vartype is found, the other
2778 // type is no longer checked.
2779 for (class_T *super = cl; super != NULL; super = super->class_extends)
2780 {
2781 class_T *cl_tmp = NULL;
2782 ocmember_T *m = NULL;
2783 if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT)
2784 {
2785 if ((m = object_member_lookup(super, name, len, NULL)) != NULL)
2786 {
2787 cl_tmp = super;
2788 vartype = VAR_OBJECT;
2789 }
2790 }
2791 if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS)
2792 {
2793 if (( m = class_member_lookup(super, name, len, NULL)) != NULL)
2794 {
2795 cl_tmp = super;
2796 vartype = VAR_OBJECT;
2797 }
2798 }
2799 if (cl_tmp == NULL)
2800 break; // member is not in this or any super class.
2801 cl_found = cl_tmp;
2802 m_found = m;
2803 }
2804 if (p_m != NULL)
2805 *p_m = m_found;
2806 return cl_found;
2807}
2808
2809/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002810 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
2811 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
2812 *
2813 * Returns a pointer to the method structure if variable is found.
2814 * Otherwise returns NULL. The method variable index is set in "*idx".
2815 */
2816 ufunc_T *
2817method_lookup(
2818 class_T *cl,
2819 vartype_T v_type,
2820 char_u *name,
2821 size_t namelen,
2822 int *idx)
2823{
2824 if (v_type == VAR_CLASS)
2825 return class_method_lookup(cl, name, namelen, idx);
2826 else
2827 return object_method_lookup(cl, name, namelen, idx);
2828}
2829
2830/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00002831 * Return TRUE if current context "cctx_arg" is inside class "cl".
2832 * Return FALSE if not.
2833 */
2834 int
2835inside_class(cctx_T *cctx_arg, class_T *cl)
2836{
2837 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002838 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002839 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002840 return TRUE;
2841 return FALSE;
2842}
2843
2844/*
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002845 * Return TRUE if object/class variable "m" is read-only.
2846 * Also give an error message.
2847 */
2848 int
2849oc_var_check_ro(class_T *cl, ocmember_T *m)
2850{
2851 if (m->ocm_flags & (OCMFLAG_FINAL | OCMFLAG_CONST))
2852 {
2853 semsg(_(e_cannot_change_readonly_variable_str_in_class_str),
2854 m->ocm_name, cl->class_name);
2855 return TRUE;
2856 }
2857 return FALSE;
2858}
2859
2860/*
2861 * Lock all the constant object variables. Called after creating and
2862 * initializing a new object.
2863 */
2864 void
2865obj_lock_const_vars(object_T *obj)
2866{
2867 for (int i = 0; i < obj->obj_class->class_obj_member_count; i++)
2868 {
2869 ocmember_T *ocm = &obj->obj_class->class_obj_members[i];
2870 if (ocm->ocm_flags & OCMFLAG_CONST)
2871 {
2872 typval_T *mtv = ((typval_T *)(obj + 1)) + i;
2873 item_lock(mtv, DICT_MAXNEST, TRUE, TRUE);
2874 }
2875 }
2876}
2877
2878/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002879 * Make a copy of an object.
2880 */
2881 void
2882copy_object(typval_T *from, typval_T *to)
2883{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002884 if (from->vval.v_object == NULL)
2885 to->vval.v_object = NULL;
2886 else
2887 {
2888 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002889 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002890 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002891}
2892
2893/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002894 * Make a copy of a class.
2895 */
2896 void
2897copy_class(typval_T *from, typval_T *to)
2898{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002899 if (from->vval.v_class == NULL)
2900 to->vval.v_class = NULL;
2901 else
2902 {
2903 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002904 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002905 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002906}
2907
2908/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002909 * Free the class "cl" and its contents.
2910 */
2911 static void
2912class_free(class_T *cl)
2913{
2914 // Freeing what the class contains may recursively come back here.
2915 // Clear "class_name" first, if it is NULL the class does not need to
2916 // be freed.
2917 VIM_CLEAR(cl->class_name);
2918
2919 class_unref(cl->class_extends);
2920
2921 for (int i = 0; i < cl->class_interface_count; ++i)
2922 {
2923 vim_free(((char_u **)cl->class_interfaces)[i]);
2924 if (cl->class_interfaces_cl[i] != NULL)
2925 class_unref(cl->class_interfaces_cl[i]);
2926 }
2927 vim_free(cl->class_interfaces);
2928 vim_free(cl->class_interfaces_cl);
2929
2930 itf2class_T *next;
2931 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2932 {
2933 next = i2c->i2c_next;
2934 vim_free(i2c);
2935 }
2936
2937 for (int i = 0; i < cl->class_class_member_count; ++i)
2938 {
2939 ocmember_T *m = &cl->class_class_members[i];
2940 vim_free(m->ocm_name);
2941 vim_free(m->ocm_init);
2942 if (cl->class_members_tv != NULL)
2943 clear_tv(&cl->class_members_tv[i]);
2944 }
2945 vim_free(cl->class_class_members);
2946 vim_free(cl->class_members_tv);
2947
2948 for (int i = 0; i < cl->class_obj_member_count; ++i)
2949 {
2950 ocmember_T *m = &cl->class_obj_members[i];
2951 vim_free(m->ocm_name);
2952 vim_free(m->ocm_init);
2953 }
2954 vim_free(cl->class_obj_members);
2955
2956 for (int i = 0; i < cl->class_class_function_count; ++i)
2957 {
2958 ufunc_T *uf = cl->class_class_functions[i];
2959 func_clear_free(uf, FALSE);
2960 }
2961 vim_free(cl->class_class_functions);
2962
2963 for (int i = 0; i < cl->class_obj_method_count; ++i)
2964 {
2965 ufunc_T *uf = cl->class_obj_methods[i];
2966 func_clear_free(uf, FALSE);
2967 }
2968 vim_free(cl->class_obj_methods);
2969
2970 clear_type_list(&cl->class_type_list);
2971
2972 class_cleared(cl);
2973
2974 vim_free(cl);
2975}
2976
2977/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002978 * Unreference a class. Free it when the reference count goes down to zero.
2979 */
2980 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002981class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002982{
Bram Moolenaard505d172022-12-18 21:42:55 +00002983 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002984 class_free(cl);
2985}
2986
2987/*
2988 * Go through the list of all classes and free items without "copyID".
2989 */
2990 int
2991class_free_nonref(int copyID)
2992{
2993 int did_free = FALSE;
2994
2995 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002996 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002997 next_nonref_class = cl->class_next_used;
2998 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002999 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003000 // Free the class and items it contains.
3001 class_free(cl);
3002 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003003 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003004 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003005
3006 next_nonref_class = NULL;
3007 return did_free;
3008}
3009
3010 int
3011set_ref_in_classes(int copyID)
3012{
3013 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3014 set_ref_in_item_class(cl, copyID, NULL, NULL);
3015
3016 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003017}
3018
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003019static object_T *first_object = NULL;
3020
3021/*
3022 * Call this function when an object has been created. It will be added to the
3023 * list headed by "first_object".
3024 */
3025 void
3026object_created(object_T *obj)
3027{
3028 if (first_object != NULL)
3029 {
3030 obj->obj_next_used = first_object;
3031 first_object->obj_prev_used = obj;
3032 }
3033 first_object = obj;
3034}
3035
3036/*
3037 * Call this function when an object has been cleared and is about to be freed.
3038 * It is removed from the list headed by "first_object".
3039 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003040 static void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003041object_cleared(object_T *obj)
3042{
3043 if (obj->obj_next_used != NULL)
3044 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
3045 if (obj->obj_prev_used != NULL)
3046 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
3047 else if (first_object == obj)
3048 first_object = obj->obj_next_used;
3049}
3050
3051/*
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003052 * Free the contents of an object ignoring the reference count.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003053 */
3054 static void
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003055object_free_contents(object_T *obj)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003056{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003057 class_T *cl = obj->obj_class;
3058
3059 if (!cl)
3060 return;
3061
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003062 // Avoid a recursive call, it can happen if "obj" has a circular reference.
3063 obj->obj_refcount = INT_MAX;
3064
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003065 // the member values are just after the object structure
3066 typval_T *tv = (typval_T *)(obj + 1);
3067 for (int i = 0; i < cl->class_obj_member_count; ++i)
3068 clear_tv(tv + i);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003069}
3070
3071 static void
3072object_free_object(object_T *obj)
3073{
3074 class_T *cl = obj->obj_class;
3075
3076 if (!cl)
3077 return;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003078
3079 // Remove from the list headed by "first_object".
3080 object_cleared(obj);
3081
3082 vim_free(obj);
3083 class_unref(cl);
3084}
3085
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003086 static void
3087object_free(object_T *obj)
3088{
3089 if (in_free_unref_items)
3090 return;
3091
3092 object_free_contents(obj);
3093 object_free_object(obj);
3094}
3095
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003096/*
3097 * Unreference an object.
3098 */
3099 void
3100object_unref(object_T *obj)
3101{
3102 if (obj != NULL && --obj->obj_refcount <= 0)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003103 object_free(obj);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003104}
3105
3106/*
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003107 * Go through the list of all objects and free items without "copyID".
3108 */
3109 int
3110object_free_nonref(int copyID)
3111{
3112 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003113
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003114 for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003115 {
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003116 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3117 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003118 // Free the object contents. Object itself will be freed later.
3119 object_free_contents(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003120 did_free = TRUE;
3121 }
3122 }
3123
3124 return did_free;
3125}
3126
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003127 void
3128object_free_items(int copyID)
3129{
3130 object_T *obj_next;
3131
3132 for (object_T *obj = first_object; obj != NULL; obj = obj_next)
3133 {
3134 obj_next = obj->obj_next_used;
3135 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3136 object_free_object(obj);
3137 }
3138}
3139
LemonBoyafe04662023-08-23 21:08:11 +02003140/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003141 * Output message which takes a variable name and the class that defines it.
3142 * "cl" is that class where the name was found. Search "cl"'s hierarchy to
3143 * find the defining class.
3144 */
3145 void
3146emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl)
3147{
3148 ocmember_T *m;
3149 class_T *cl_def = class_defining_member(cl, name, len, &m);
3150 if (cl_def != NULL)
3151 semsg(_(msg), m->ocm_name, cl_def->class_name);
3152 else
3153 emsg(_(e_internal_error_please_report_a_bug));
3154}
3155
3156/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003157 * Echo a class or object method not found message.
3158 */
3159 void
3160method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3161{
3162 char_u *method_name = vim_strnsave(name, len);
3163 if ((v_type == VAR_OBJECT)
3164 && (class_method_idx(cl, name, len) >= 0))
3165 {
3166 // If this is a class method, then give a different error
3167 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003168 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003169 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003170 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003171 method_name, cl->class_name);
3172 }
3173 else if ((v_type == VAR_CLASS)
3174 && (object_method_idx(cl, name, len) >= 0))
3175 {
3176 // If this is an object method, then give a different error
3177 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003178 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003179 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003180 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003181 method_name, cl->class_name);
3182 }
3183 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003184 semsg(_(e_method_not_found_on_class_str_str), method_name,
3185 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003186 vim_free(method_name);
3187}
3188
3189/*
3190 * Echo a class or object member not found message.
3191 */
3192 void
3193member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3194{
3195 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
3196
3197 if (v_type == VAR_OBJECT)
3198 {
3199 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003200 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003201 varname, cl->class_name);
3202 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003203 semsg(_(e_variable_not_found_on_object_str_str), varname,
3204 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003205 }
3206 else
3207 {
3208 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003209 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003210 varname, cl->class_name);
3211 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003212 semsg(_(e_class_variable_str_not_found_in_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003213 varname, cl->class_name);
3214 }
3215 vim_free(varname);
3216}
3217
3218/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02003219 * Return TRUE when the class "cl", its base class or one of the implemented
3220 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02003221 */
3222 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003223class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02003224{
3225 if (cl == other_cl)
3226 return TRUE;
3227
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003228 // Recursively check the base classes.
3229 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02003230 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003231 if (cl == other_cl)
3232 return TRUE;
3233 // Check the implemented interfaces and the super interfaces
3234 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003235 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003236 class_T *intf = cl->class_interfaces_cl[i];
3237 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003238 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003239 if (intf == other_cl)
3240 return TRUE;
3241 // check the super interfaces
3242 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003243 }
3244 }
LemonBoyafe04662023-08-23 21:08:11 +02003245 }
3246
3247 return FALSE;
3248}
3249
3250/*
Ernie Rael2025af12023-12-12 16:58:00 +01003251 * "instanceof(object, classinfo, ...)" function
LemonBoyafe04662023-08-23 21:08:11 +02003252 */
3253 void
3254f_instanceof(typval_T *argvars, typval_T *rettv)
3255{
3256 typval_T *object_tv = &argvars[0];
3257 typval_T *classinfo_tv = &argvars[1];
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02003258 class_T *c;
LemonBoyafe04662023-08-23 21:08:11 +02003259
3260 rettv->vval.v_number = VVAL_FALSE;
3261
3262 if (check_for_object_arg(argvars, 0) == FAIL
Ernie Rael2025af12023-12-12 16:58:00 +01003263 || check_for_class_or_typealias_args(argvars, 1) == FAIL)
LemonBoyafe04662023-08-23 21:08:11 +02003264 return;
3265
Ernie Rael3da696d2023-09-19 20:14:18 +02003266 if (object_tv->vval.v_object == NULL)
3267 return;
3268
Ernie Rael2025af12023-12-12 16:58:00 +01003269 for (; classinfo_tv->v_type != VAR_UNKNOWN; ++classinfo_tv)
LemonBoyafe04662023-08-23 21:08:11 +02003270 {
Ernie Rael2025af12023-12-12 16:58:00 +01003271 if (classinfo_tv->v_type == VAR_TYPEALIAS)
3272 c = classinfo_tv->vval.v_typealias->ta_type->tt_class;
3273 else
3274 c = classinfo_tv->vval.v_class;
3275
3276 if (class_instance_of(object_tv->vval.v_object->obj_class, c))
LemonBoyafe04662023-08-23 21:08:11 +02003277 {
Ernie Rael2025af12023-12-12 16:58:00 +01003278 rettv->vval.v_number = VVAL_TRUE;
3279 return;
LemonBoyafe04662023-08-23 21:08:11 +02003280 }
3281 }
LemonBoyafe04662023-08-23 21:08:11 +02003282}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00003283
3284#endif // FEAT_EVAL