blob: a05dcce3da8256fcb647901c4c7767c6ec658147 [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
155/*
156 * Add a member to an object or a class.
157 * Returns OK when successful, "init_expr" will be consumed then.
158 * Returns FAIL otherwise, caller might need to free "init_expr".
159 */
160 static int
161add_member(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200162 garray_T *gap,
163 char_u *varname,
164 char_u *varname_end,
165 int has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200166 int has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200167 type_T *type,
168 char_u *init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +0000169{
170 if (ga_grow(gap, 1) == FAIL)
171 return FAIL;
172 ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
173 m->ocm_name = vim_strnsave(varname, varname_end - varname);
=?UTF-8?q?Ola=20S=C3=B6der?=d8742472023-03-05 13:12:32 +0000174 m->ocm_access = has_public ? VIM_ACCESS_ALL
175 : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200176 m->ocm_has_type = has_type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000177 m->ocm_type = type;
178 if (init_expr != NULL)
179 m->ocm_init = init_expr;
180 ++gap->ga_len;
181 return OK;
182}
183
184/*
185 * Move the class or object members found while parsing a class into the class.
186 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000187 * "parent_members" points to the members in the parent class (if any)
188 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000189 * "members" will be set to the newly allocated array of members and
190 * "member_count" set to the number of members.
191 * Returns OK or FAIL.
192 */
193 static int
194add_members_to_class(
195 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000196 ocmember_T *parent_members,
197 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000198 ocmember_T **members,
199 int *member_count)
200{
Bram Moolenaar83677162023-01-08 19:54:10 +0000201 *member_count = parent_count + gap->ga_len;
202 *members = *member_count == 0 ? NULL
203 : ALLOC_MULT(ocmember_T, *member_count);
204 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000205 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000206 for (int i = 0; i < parent_count; ++i)
207 {
208 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000209 ocmember_T *m = *members + i;
210 *m = parent_members[i];
211 m->ocm_name = vim_strsave(m->ocm_name);
212 if (m->ocm_init != NULL)
213 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000214 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000215 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000216 // new members are moved
217 mch_memmove(*members + parent_count,
218 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000219 VIM_CLEAR(gap->ga_data);
220 return OK;
221}
222
223/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000224 * Convert a member index "idx" of interface "itf" to the member index of class
225 * "cl" implementing that interface.
226 */
227 int
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200228object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000229{
Ernie Rael18143d32023-09-04 22:30:41 +0200230 if (idx >= (is_method ? itf->class_obj_method_count
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200231 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000232 {
233 siemsg("index %d out of range for interface %s", idx, itf->class_name);
234 return 0;
235 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200236
237 // If "cl" is the interface or the class that is extended, then the method
238 // index can be used directly and there is no need to search for the method
239 // index in one of the child classes.
240 if (cl == itf)
241 return idx;
242
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200243 itf2class_T *i2c = NULL;
244 int searching = TRUE;
245 int method_offset = 0;
246
Ernie Raelcf138d42023-09-06 20:45:03 +0200247 for (class_T *super = cl; super != NULL && searching;
248 super = super->class_extends)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200249 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200250 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200251 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200252 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
253 {
254 searching = FALSE;
255 break;
256 }
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200257 }
258 if (searching && is_method)
259 // The parent class methods are stored after the current class
260 // methods.
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200261 method_offset += super->class_obj_method_count_child;
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200262 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000263 if (i2c == NULL)
264 {
265 siemsg("class %s not found on interface %s",
266 cl->class_name, itf->class_name);
267 return 0;
268 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +0200269
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200270 // A table follows the i2c for the class
271 int *table = (int *)(i2c + 1);
272 // "method_offset" is 0, if method is in the current class. If method
273 // is in a parent class, then it is non-zero.
274 return table[idx] + method_offset;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000275}
276
277/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200278 * Check whether a class named "extends_name" is present. If the class is
279 * valid, then "extends_clp" is set with the class pointer.
280 * Returns TRUE if the class name "extends_names" is a valid class.
281 */
282 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200283validate_extends_class(
284 char_u *extends_name,
285 class_T **extends_clp,
286 int is_class)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200287{
288 typval_T tv;
289 int success = FALSE;
290
291 tv.v_type = VAR_UNKNOWN;
292 if (eval_variable_import(extends_name, &tv) == FAIL)
293 {
294 semsg(_(e_class_name_not_found_str), extends_name);
295 return success;
296 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200297
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200298 if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
299 || (is_class
300 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
301 || (!is_class
302 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0))
303 // a interface cannot extend a class and a class cannot extend an
304 // interface.
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200305 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200306 else
307 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200308 class_T *extends_cl = tv.vval.v_class;
309 ++extends_cl->class_refcount;
310 *extends_clp = extends_cl;
311 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200312 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200313 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200314
315 return success;
316}
317
318/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200319 * Check method names in the parent class lineage to make sure the access is
320 * the same for overridden methods.
321 */
322 static int
323validate_extends_methods(
324 garray_T *objmethods_gap,
325 class_T *extends_cl)
326{
327 class_T *super = extends_cl;
328 int method_count = objmethods_gap->ga_len;
329 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
330
331 while (super != NULL)
332 {
333 int extends_method_count = super->class_obj_method_count_child;
334 if (extends_method_count == 0)
335 {
336 super = super->class_extends;
337 continue;
338 }
339
340 ufunc_T **extends_methods = super->class_obj_methods;
341
342 for (int i = 0; i < extends_method_count; i++)
343 {
344 char_u *pstr = extends_methods[i]->uf_name;
345 int extends_private = (*pstr == '_');
346 if (extends_private)
347 pstr++;
348
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200349 // When comparing the method names, ignore the access type (public
350 // and private methods are considered the same).
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200351 for (int j = 0; j < method_count; j++)
352 {
353 char_u *qstr = cl_fp[j]->uf_name;
354 int priv_method = (*qstr == '_');
355 if (priv_method)
356 qstr++;
357 if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
358 {
359 // Method access is different between the super class and
360 // the subclass
361 semsg(_(e_method_str_of_class_str_has_different_access),
362 cl_fp[j]->uf_name, super->class_name);
363 return FALSE;
364 }
365 }
366 }
367 super = super->class_extends;
368 }
369
370 return TRUE;
371}
372
373/*
374 * Check whether a object member variable in "objmembers_gap" is a duplicate of
375 * a member in any of the extended parent class lineage. Returns TRUE if there
376 * are no duplicates.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200377 */
378 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200379extends_check_dup_members(
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200380 garray_T *objmembers_gap,
381 class_T *extends_cl)
382{
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200383 int member_count = objmembers_gap->ga_len;
384 if (member_count == 0)
385 return TRUE;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200386
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200387 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
388
389 // Validate each member variable
390 for (int c_i = 0; c_i < member_count; c_i++)
391 {
392 class_T *p_cl = extends_cl;
393 ocmember_T *c_m = members + c_i;
394 char_u *pstr = (*c_m->ocm_name == '_')
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200395 ? c_m->ocm_name + 1 : c_m->ocm_name;
396
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200397 // Check in all the parent classes in the lineage
398 while (p_cl != NULL)
399 {
400 int p_member_count = p_cl->class_obj_member_count;
401 if (p_member_count == 0)
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200402 {
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200403 p_cl = p_cl->class_extends;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200404 continue;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200405 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200406 ocmember_T *p_members = p_cl->class_obj_members;
407
408 // Compare against all the members in the parent class
409 for (int p_i = 0; p_i < p_member_count; p_i++)
410 {
411 ocmember_T *p_m = p_members + p_i;
412 char_u *qstr = (*p_m->ocm_name == '_')
413 ? p_m->ocm_name + 1 : p_m->ocm_name;
414 if (STRCMP(pstr, qstr) == 0)
415 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200416 semsg(_(e_duplicate_variable_str), c_m->ocm_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200417 return FALSE;
418 }
419 }
420
421 p_cl = p_cl->class_extends;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200422 }
423 }
424
425 return TRUE;
426}
427
428/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200429 * Compare the variable type of interface variables in "objmembers_gap" against
430 * the variable in any of the extended super interface lineage. Used to
431 * compare the variable types when extending interfaces. Returns TRUE if the
432 * variable types are the same.
433 */
434 static int
435extends_check_intf_var_type(
436 garray_T *objmembers_gap,
437 class_T *extends_cl)
438{
439 int member_count = objmembers_gap->ga_len;
440 if (member_count == 0)
441 return TRUE;
442
443 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
444
445 // Validate each member variable
446 for (int c_i = 0; c_i < member_count; c_i++)
447 {
448 class_T *p_cl = extends_cl;
449 ocmember_T *c_m = members + c_i;
450 int var_found = FALSE;
451
452 // Check in all the parent classes in the lineage
453 while (p_cl != NULL && !var_found)
454 {
455 int p_member_count = p_cl->class_obj_member_count;
456 if (p_member_count == 0)
457 {
458 p_cl = p_cl->class_extends;
459 continue;
460 }
461 ocmember_T *p_members = p_cl->class_obj_members;
462
463 // Compare against all the members in the parent class
464 for (int p_i = 0; p_i < p_member_count; p_i++)
465 {
466 where_T where = WHERE_INIT;
467 ocmember_T *p_m = p_members + p_i;
468
469 if (STRCMP(p_m->ocm_name, c_m->ocm_name) != 0)
470 continue;
471
472 // Ensure the type is matching.
473 where.wt_func_name = (char *)c_m->ocm_name;
474 where.wt_kind = WT_MEMBER;
475
476 if (check_type(p_m->ocm_type, c_m->ocm_type, TRUE,
477 where) == FAIL)
478 return FALSE;
479
480 var_found = TRUE;
481 }
482
483 p_cl = p_cl->class_extends;
484 }
485 }
486
487 return TRUE;
488}
489
490/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200491 * When extending an abstract class, check whether all the abstract methods in
492 * the parent class are implemented. Returns TRUE if all the methods are
493 * implemented.
494 */
495 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200496validate_abstract_class_methods(
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200497 garray_T *classmethods_gap,
498 garray_T *objmethods_gap,
499 class_T *extends_cl)
500{
501 for (int loop = 1; loop <= 2; ++loop)
502 {
503 // loop == 1: check class methods
504 // loop == 2: check object methods
505 int extends_method_count = loop == 1
506 ? extends_cl->class_class_function_count
507 : extends_cl->class_obj_method_count;
508 if (extends_method_count == 0)
509 continue;
510
511 ufunc_T **extends_methods = loop == 1
512 ? extends_cl->class_class_functions
513 : extends_cl->class_obj_methods;
514
515 int method_count = loop == 1 ? classmethods_gap->ga_len
516 : objmethods_gap->ga_len;
517 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
518 ? classmethods_gap->ga_data
519 : objmethods_gap->ga_data);
520
521 for (int i = 0; i < extends_method_count; i++)
522 {
523 ufunc_T *uf = extends_methods[i];
Yegappan Lakshmanan1db15142023-09-19 20:34:05 +0200524 if (!IS_ABSTRACT_METHOD(uf))
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200525 continue;
526
527 int method_found = FALSE;
528
529 for (int j = 0; j < method_count; j++)
530 {
531 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
532 {
533 method_found = TRUE;
534 break;
535 }
536 }
537
538 if (!method_found)
539 {
540 semsg(_(e_abstract_method_str_not_found), uf->uf_name);
541 return FALSE;
542 }
543 }
544 }
545
546 return TRUE;
547}
548
549/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200550 * Returns TRUE if the interface variable "if_var" is present in the list of
551 * variables in "cl_mt" or in the parent lineage of one of the extended classes
552 * in "extends_cl". For a class variable, 'is_class_var' is TRUE.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200553 */
554 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200555intf_variable_present(
556 char_u *intf_class_name,
557 ocmember_T *if_var,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200558 ocmember_T *cl_mt,
559 int cl_member_count,
560 class_T *extends_cl)
561{
562 int variable_present = FALSE;
563
564 for (int cl_i = 0; cl_i < cl_member_count; ++cl_i)
565 {
566 ocmember_T *m = &cl_mt[cl_i];
567 where_T where = WHERE_INIT;
568
569 if (STRCMP(if_var->ocm_name, m->ocm_name) != 0)
570 continue;
571
572 // Ensure the access type is same
573 if (if_var->ocm_access != m->ocm_access)
574 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200575 semsg(_(e_variable_str_of_interface_str_has_different_access),
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200576 if_var->ocm_name, intf_class_name);
577 return FALSE;
578 }
579
580 // Ensure the type is matching.
581 if (m->ocm_type == &t_any)
582 {
583 // variable type is not specified. Use the variable type in the
584 // interface.
585 m->ocm_type = if_var->ocm_type;
586 }
587 else
588 {
589 where.wt_func_name = (char *)m->ocm_name;
590 where.wt_kind = WT_MEMBER;
591 if (check_type(if_var->ocm_type, m->ocm_type, TRUE,
592 where) == FAIL)
593 return FALSE;
594 }
595
596 variable_present = TRUE;
597 break;
598 }
599
600 if (!variable_present && extends_cl != NULL)
601 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200602 int ext_cl_count = extends_cl->class_obj_member_count;
603 ocmember_T *ext_cl_mt = extends_cl->class_obj_members;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200604 return intf_variable_present(intf_class_name, if_var,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200605 ext_cl_mt, ext_cl_count,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200606 extends_cl->class_extends);
607 }
608
609 return variable_present;
610}
611
612/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200613 * Check the variables of the interface class "ifcl" match object variables
614 * ("objmembers_gap") of a class.
615 * Returns TRUE if the object variables names are valid.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200616 */
617 static int
618validate_interface_variables(
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200619 char_u *intf_class_name,
620 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200621 garray_T *objmembers_gap,
622 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200623{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200624 int if_count = ifcl->class_obj_member_count;
625 if (if_count == 0)
626 return TRUE;
627
628 ocmember_T *if_ms = ifcl->class_obj_members;
629 ocmember_T *cl_ms = (ocmember_T *)(objmembers_gap->ga_data);
630 int cl_count = objmembers_gap->ga_len;
631 for (int if_i = 0; if_i < if_count; ++if_i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200632 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200633 if (!intf_variable_present(intf_class_name, &if_ms[if_i], cl_ms,
634 cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200635 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200636 semsg(_(e_variable_str_of_interface_str_not_implemented),
637 if_ms[if_i].ocm_name, intf_class_name);
638 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200639 }
640 }
641
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200642 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200643}
644
645/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200646 * Returns TRUE if the method signature of "if_method" and "cl_method" matches.
647 */
648 static int
649intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method)
650{
651 where_T where = WHERE_INIT;
652
653 // Ensure the type is matching.
654 where.wt_func_name = (char *)if_method->uf_name;
655 where.wt_kind = WT_METHOD;
656 if (check_type(if_method->uf_func_type, cl_method->uf_func_type, TRUE,
657 where) == FAIL)
658 return FALSE;
659
660 return TRUE;
661}
662
663/*
664 * Returns TRUE if the interface method "if_ufunc" is present in the list of
665 * methods in "cl_fp" or in the parent lineage of one of the extended classes
666 * in "extends_cl". For a class method, 'is_class_method' is TRUE.
667 */
668 static int
669intf_method_present(
670 ufunc_T *if_ufunc,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200671 ufunc_T **cl_fp,
672 int cl_count,
673 class_T *extends_cl)
674{
675 int method_present = FALSE;
676
677 for (int cl_i = 0; cl_i < cl_count; ++cl_i)
678 {
679 char_u *cl_name = cl_fp[cl_i]->uf_name;
680 if (STRCMP(if_ufunc->uf_name, cl_name) == 0)
681 {
682 // Ensure the type is matching.
683 if (!intf_method_type_matches(if_ufunc, cl_fp[cl_i]))
684 return FALSE;
685 method_present = TRUE;
686 break;
687 }
688 }
689
690 if (!method_present && extends_cl != NULL)
691 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200692 ufunc_T **ext_cl_fp = (ufunc_T **)(extends_cl->class_obj_methods);
693 int ext_cl_count = extends_cl->class_obj_method_count;
694 return intf_method_present(if_ufunc, ext_cl_fp, ext_cl_count,
695 extends_cl->class_extends);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200696 }
697
698 return method_present;
699}
700
701/*
702 * Validate that a new class implements all the class/instance methods in the
703 * interface "ifcl". The new class methods are in "classfunctions_gap" and the
704 * new object methods are in "objmemthods_gap". Also validates the method
705 * types.
706 * Returns TRUE if all the interface class/object methods are implemented in
707 * the new class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200708 */
709 static int
710validate_interface_methods(
711 char_u *intf_class_name,
712 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200713 garray_T *objmethods_gap,
714 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200715{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200716 int if_count = ifcl->class_obj_method_count;
717 if (if_count == 0)
718 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200719
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200720 ufunc_T **if_fp = ifcl->class_obj_methods;
721 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
722 int cl_count = objmethods_gap->ga_len;
723 for (int if_i = 0; if_i < if_count; ++if_i)
724 {
725 char_u *if_name = if_fp[if_i]->uf_name;
726
727 if (!intf_method_present(if_fp[if_i], cl_fp, cl_count, extends_cl))
728 {
729 semsg(_(e_method_str_of_interface_str_not_implemented),
730 if_name, intf_class_name);
731 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200732 }
733 }
734
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200735 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200736}
737
738/*
739 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200740 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200741 * object methods and object members in the new class are in
742 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
743 * "objmembers_gap" respectively.
744 */
745 static int
746validate_implements_classes(
747 garray_T *impl_gap,
748 class_T **intf_classes,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200749 garray_T *objmethods_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200750 garray_T *objmembers_gap,
751 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200752{
753 int success = TRUE;
754
755 for (int i = 0; i < impl_gap->ga_len && success; ++i)
756 {
757 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
758 typval_T tv;
759 tv.v_type = VAR_UNKNOWN;
760 if (eval_variable_import(impl, &tv) == FAIL)
761 {
762 semsg(_(e_interface_name_not_found_str), impl);
763 success = FALSE;
764 break;
765 }
766
767 if (tv.v_type != VAR_CLASS
768 || tv.vval.v_class == NULL
769 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
770 {
771 semsg(_(e_not_valid_interface_str), impl);
772 success = FALSE;
773 clear_tv(&tv);
774 break;
775 }
776
777 class_T *ifcl = tv.vval.v_class;
778 intf_classes[i] = ifcl;
779 ++ifcl->class_refcount;
780
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200781 // check the variables of the interface match the members of the class
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200782 success = validate_interface_variables(impl, ifcl, objmembers_gap,
783 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200784
785 // check the functions/methods of the interface match the
786 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200787 if (success)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200788 success = validate_interface_methods(impl, ifcl, objmethods_gap,
789 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200790 clear_tv(&tv);
791 }
792
793 return success;
794}
795
796/*
797 * Check no function argument name is used as a class member.
798 * (Object members are always accessed with "this." prefix, so no need
799 * to check them.)
800 */
801 static int
802check_func_arg_names(
803 garray_T *classfunctions_gap,
804 garray_T *objmethods_gap,
805 garray_T *classmembers_gap)
806{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200807 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200808 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200809 {
810 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
811
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200812 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200813 {
814 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
815
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200816 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200817 {
818 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
819 garray_T *mgap = classmembers_gap;
820
821 // Check all the class member names
822 for (int mi = 0; mi < mgap->ga_len; ++mi)
823 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200824 char_u *mname =
825 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200826 if (STRCMP(aname, mname) == 0)
827 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200828 if (uf->uf_script_ctx.sc_sid > 0)
829 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
830
831 semsg(_(e_argument_already_declared_in_class_str),
832 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200833
834 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200835 }
836 }
837 }
838 }
839 }
840
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200841 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200842}
843
844/*
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200845 * Returns TRUE if 'varname' is a reserved keyword name
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200846 */
847 static int
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200848is_reserved_varname(char_u *varname, char_u *varname_end)
849{
850 int reserved = FALSE;
851 char_u save_varname_end = *varname_end;
852 *varname_end = NUL;
853
854 reserved = check_reserved_name(varname, FALSE) == FAIL;
855
856 *varname_end = save_varname_end;
857
858 return reserved;
859}
860
861/*
862 * Returns TRUE if the variable "varname" is already defined either as a class
863 * variable or as an object variable.
864 */
865 static int
866is_duplicate_variable(
867 garray_T *class_members,
868 garray_T *obj_members,
869 char_u *varname,
870 char_u *varname_end)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200871{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200872 char_u *name = vim_strnsave(varname, varname_end - varname);
873 char_u *pstr = (*name == '_') ? name + 1 : name;
874 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200875
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200876 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200877 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200878 // loop == 1: class variables, loop == 2: object variables
879 garray_T *vgap = (loop == 1) ? class_members : obj_members;
880 for (int i = 0; i < vgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200881 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200882 ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i;
883 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1
884 : m->ocm_name;
885 if (STRCMP(pstr, qstr) == 0)
886 {
887 semsg(_(e_duplicate_variable_str), name);
888 dup = TRUE;
889 break;
890 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200891 }
892 }
893
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200894 vim_free(name);
895 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200896}
897
898/*
899 * Returns TRUE if the method "name" is already defined.
900 */
901 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200902is_duplicate_method(
903 garray_T *classmethods_gap,
904 garray_T *objmethods_gap,
905 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200906{
907 char_u *pstr = (*name == '_') ? name + 1 : name;
908
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200909 // loop 1: class methods, loop 2: object methods
910 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200911 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200912 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
913 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200914 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200915 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
916 char_u *qstr = *n == '_' ? n + 1 : n;
917 if (STRCMP(pstr, qstr) == 0)
918 {
919 semsg(_(e_duplicate_function_str), name);
920 return TRUE;
921 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200922 }
923 }
924
925 return FALSE;
926}
927
928/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200929 * Returns TRUE if the constructor is valid.
930 */
931 static int
932is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
933{
934 // Constructors are not allowed in abstract classes.
935 if (is_abstract)
936 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200937 emsg(_(e_cannot_define_new_method_in_abstract_class));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200938 return FALSE;
939 }
940 // A constructor is always static, no need to define it so.
941 if (has_static)
942 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200943 emsg(_(e_cannot_define_new_method_as_static));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200944 return FALSE;
945 }
946 // A return type should not be specified for the new()
947 // constructor method.
948 if (uf->uf_ret_type->tt_type != VAR_VOID)
949 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200950 emsg(_(e_cannot_use_a_return_type_with_new_method));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200951 return FALSE;
952 }
953 return TRUE;
954}
955
956/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200957 * Update the interface class lookup table for the member index on the
958 * interface to the member index in the class implementing the interface.
959 * And a lookup table for the object method index on the interface
960 * to the object method index in the class implementing the interface.
961 * This is also used for updating the lookup table for the extended class
962 * hierarchy.
963 */
964 static int
965update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200966 class_T *ifcl,
967 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200968 garray_T *objmethods,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200969 int pobj_method_offset)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200970{
971 if (ifcl == NULL)
972 return OK;
973
974 // Table for members.
975 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200976 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200977 if (if2cl == NULL)
978 return FAIL;
979 if2cl->i2c_next = ifcl->class_itf2class;
980 ifcl->class_itf2class = if2cl;
981 if2cl->i2c_class = cl;
982 if2cl->i2c_is_method = FALSE;
983
984 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
985 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
986 {
987 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200988 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200989 {
990 int *table = (int *)(if2cl + 1);
991 table[if_i] = cl_i;
992 break;
993 }
994 }
995
996 // Table for methods.
997 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200998 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200999 if (if2cl == NULL)
1000 return FAIL;
1001 if2cl->i2c_next = ifcl->class_itf2class;
1002 ifcl->class_itf2class = if2cl;
1003 if2cl->i2c_class = cl;
1004 if2cl->i2c_is_method = TRUE;
1005
1006 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
1007 {
1008 int done = FALSE;
1009 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
1010 {
1011 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001012 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001013 {
1014 int *table = (int *)(if2cl + 1);
1015 table[if_i] = cl_i;
1016 done = TRUE;
1017 break;
1018 }
1019 }
1020
1021 // extended class object method is not overridden by the child class.
1022 // Keep the method declared in one of the parent classes in the
1023 // lineage.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001024 if (!done)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001025 {
1026 // If "ifcl" is not the immediate parent of "cl", then search in
1027 // the intermediate parent classes.
1028 if (cl->class_extends != ifcl)
1029 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001030 class_T *parent = cl->class_extends;
1031 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001032
1033 while (!done && parent != NULL && parent != ifcl)
1034 {
1035
1036 for (int cl_i = 0;
1037 cl_i < parent->class_obj_method_count_child; ++cl_i)
1038 {
1039 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
1040 parent->class_obj_methods[cl_i]->uf_name)
1041 == 0)
1042 {
1043 int *table = (int *)(if2cl + 1);
1044 table[if_i] = method_offset + cl_i;
1045 done = TRUE;
1046 break;
1047 }
1048 }
1049 method_offset += parent->class_obj_method_count_child;
1050 parent = parent->class_extends;
1051 }
1052 }
1053
1054 if (!done)
1055 {
1056 int *table = (int *)(if2cl + 1);
1057 table[if_i] = pobj_method_offset + if_i;
1058 }
1059 }
1060 }
1061
1062 return OK;
1063}
1064
1065/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001066 * Update the member and object method lookup tables for a new class in the
1067 * interface class.
1068 * For each interface add a lookup table for the member index on the interface
1069 * to the member index in the new class. And a lookup table for the object
1070 * method index on the interface to the object method index in the new class.
1071 */
1072 static int
1073add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
1074{
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001075 // update the lookup table for all the implemented interfaces
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001076 for (int i = 0; i < cl->class_interface_count; ++i)
1077 {
1078 class_T *ifcl = cl->class_interfaces_cl[i];
1079
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001080 // update the lookup table for this interface and all its super
1081 // interfaces.
1082 while (ifcl != NULL)
1083 {
1084 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
1085 0) == FAIL)
1086 return FAIL;
1087 ifcl = ifcl->class_extends;
1088 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001089 }
1090
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001091 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001092 if (extends_cl != NULL)
1093 {
1094 class_T *pclass = extends_cl;
1095 int pobj_method_offset = objmethods_gap->ga_len;
1096
1097 // Update the entire lineage of extended classes.
1098 while (pclass != NULL)
1099 {
1100 if (update_member_method_lookup_table(pclass, cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001101 objmethods_gap, pobj_method_offset) == FAIL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001102 return FAIL;
1103
1104 pobj_method_offset += pclass->class_obj_method_count_child;
1105 pclass = pclass->class_extends;
1106 }
1107 }
1108
1109 return OK;
1110}
1111
1112/*
1113 * Add class members to a new class. Allocate a typval for each class member
1114 * and initialize it.
1115 */
1116 static void
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001117add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001118{
1119 // Allocate a typval for each class member and initialize it.
1120 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
1121 cl->class_class_member_count);
1122 if (cl->class_members_tv == NULL)
1123 return;
1124
1125 for (int i = 0; i < cl->class_class_member_count; ++i)
1126 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001127 ocmember_T *m = &cl->class_class_members[i];
1128 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001129 if (m->ocm_init != NULL)
1130 {
1131 typval_T *etv = eval_expr(m->ocm_init, eap);
1132 if (etv != NULL)
1133 {
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001134 if (m->ocm_type->tt_type == VAR_ANY
1135 && !m->ocm_has_type
1136 && etv->v_type != VAR_SPECIAL)
1137 // If the member variable type is not yet set, then use
1138 // the initialization expression type.
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001139 m->ocm_type = typval2type(etv, get_copyID(),
1140 type_list_gap,
1141 TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001142 *tv = *etv;
1143 vim_free(etv);
1144 }
1145 }
1146 else
1147 {
1148 // TODO: proper default value
1149 tv->v_type = m->ocm_type->tt_type;
1150 tv->vval.v_string = NULL;
1151 }
1152 }
1153}
1154
1155/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001156 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001157 */
1158 static void
1159add_default_constructor(
1160 class_T *cl,
1161 garray_T *classfunctions_gap,
1162 garray_T *type_list_gap)
1163{
1164 garray_T fga;
1165
1166 ga_init2(&fga, 1, 1000);
1167 ga_concat(&fga, (char_u *)"new(");
1168 for (int i = 0; i < cl->class_obj_member_count; ++i)
1169 {
1170 if (i > 0)
1171 ga_concat(&fga, (char_u *)", ");
1172 ga_concat(&fga, (char_u *)"this.");
1173 ocmember_T *m = cl->class_obj_members + i;
1174 ga_concat(&fga, (char_u *)m->ocm_name);
1175 ga_concat(&fga, (char_u *)" = v:none");
1176 }
1177 ga_concat(&fga, (char_u *)")\nenddef\n");
1178 ga_append(&fga, NUL);
1179
1180 exarg_T fea;
1181 CLEAR_FIELD(fea);
1182 fea.cmdidx = CMD_def;
1183 fea.cmd = fea.arg = fga.ga_data;
1184
1185 garray_T lines_to_free;
1186 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1187
h-eastb895b0f2023-09-24 15:46:31 +02001188 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
1189 cl->class_obj_members, cl->class_obj_member_count);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001190
1191 ga_clear_strings(&lines_to_free);
1192 vim_free(fga.ga_data);
1193
1194 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1195 {
1196 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001197 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001198 ++classfunctions_gap->ga_len;
1199
1200 nf->uf_flags |= FC_NEW;
1201 nf->uf_ret_type = get_type_ptr(type_list_gap);
1202 if (nf->uf_ret_type != NULL)
1203 {
1204 nf->uf_ret_type->tt_type = VAR_OBJECT;
1205 nf->uf_ret_type->tt_class = cl;
1206 nf->uf_ret_type->tt_argcount = 0;
1207 nf->uf_ret_type->tt_args = NULL;
1208 }
1209 }
1210}
1211
1212/*
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001213 * Add the class methods and object methods to the new class "cl".
1214 * When extending a class "extends_cl", add the instance methods from the
1215 * parent class also.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001216 */
1217 static int
1218add_classfuncs_objmethods(
1219 class_T *cl,
1220 class_T *extends_cl,
1221 garray_T *classfunctions_gap,
1222 garray_T *objmethods_gap)
1223{
1224 // loop 1: class functions, loop 2: object methods
1225 for (int loop = 1; loop <= 2; ++loop)
1226 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001227 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1228 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001229 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001230 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001231 : &cl->class_obj_methods;
1232
1233 int parent_count = 0;
1234 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001235 // Include object methods from the parent.
1236 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001237 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001238 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001239 : extends_cl->class_obj_method_count;
1240
1241 *fcount = parent_count + gap->ga_len;
1242 if (*fcount == 0)
1243 {
1244 *fup = NULL;
1245 continue;
1246 }
1247 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1248 if (*fup == NULL)
1249 return FAIL;
1250
1251 if (gap->ga_len != 0)
1252 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
1253 vim_free(gap->ga_data);
1254 if (loop == 1)
1255 cl->class_class_function_count_child = gap->ga_len;
1256 else
1257 cl->class_obj_method_count_child = gap->ga_len;
1258
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001259 if (loop == 2)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001260 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001261 // Copy instance methods from the parent.
1262
1263 for (int i = 0; i < parent_count; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001264 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001265 // Can't use the same parent function, because "uf_class" is
1266 // different and compilation will have a different result.
1267 // Put them after the functions in the current class, object
1268 // methods may be overruled, then "super.Method()" is used to
1269 // find a method from the parent.
1270 ufunc_T *pf = (extends_cl->class_obj_methods)[i];
1271 (*fup)[gap->ga_len + i] = copy_function(pf);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001272
1273 // If the child class overrides a function from the parent
1274 // the signature must be equal.
1275 char_u *pname = pf->uf_name;
1276 for (int ci = 0; ci < gap->ga_len; ++ci)
1277 {
1278 ufunc_T *cf = (*fup)[ci];
1279 char_u *cname = cf->uf_name;
1280 if (STRCMP(pname, cname) == 0)
1281 {
1282 where_T where = WHERE_INIT;
1283 where.wt_func_name = (char *)pname;
1284 where.wt_kind = WT_METHOD;
1285 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1286 TRUE, where);
1287 }
1288 }
1289 }
1290 }
1291
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001292 // Set the class pointer on all the functions and object methods.
1293 for (int i = 0; i < *fcount; ++i)
1294 {
1295 ufunc_T *fp = (*fup)[i];
1296 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001297 if (i < gap->ga_len)
1298 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001299 if (loop == 2)
1300 fp->uf_flags |= FC_OBJECT;
1301 }
1302 }
1303
1304 return OK;
1305}
1306
1307/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001308 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001309 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001310 */
1311 void
1312ex_class(exarg_T *eap)
1313{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001314 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1315 long start_lnum = SOURCING_LNUM;
1316 char_u *arg = eap->arg;
1317 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001318
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001319 if (is_abstract)
1320 {
1321 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1322 {
1323 semsg(_(e_invalid_argument_str), arg);
1324 return;
1325 }
1326 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001327 is_class = TRUE;
1328 }
1329
1330 if (!current_script_is_vim9()
1331 || (cmdmod.cmod_flags & CMOD_LEGACY)
1332 || !getline_equal(eap->getline, eap->cookie, getsourceline))
1333 {
1334 if (is_class)
1335 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1336 else
1337 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1338 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001339 }
1340
1341 if (!ASCII_ISUPPER(*arg))
1342 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001343 if (is_class)
1344 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1345 else
1346 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1347 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001348 return;
1349 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001350 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1351 if (!IS_WHITE_OR_NUL(*name_end))
1352 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001353 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001354 return;
1355 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001356 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001357
Bram Moolenaara86655a2023-01-12 17:06:27 +00001358 // "export class" gets used when creating the class, don't use "is_export"
1359 // for the items inside the class.
1360 int class_export = is_export;
1361 is_export = FALSE;
1362
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001363 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001364 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001365
Bram Moolenaar83677162023-01-08 19:54:10 +00001366 // Name for "extends BaseClass"
1367 char_u *extends = NULL;
1368
Bram Moolenaar94674f22023-01-06 18:42:20 +00001369 // Names for "implements SomeInterface"
1370 garray_T ga_impl;
1371 ga_init2(&ga_impl, sizeof(char_u *), 5);
1372
1373 arg = skipwhite(name_end);
1374 while (*arg != NUL && *arg != '#' && *arg != '\n')
1375 {
1376 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001377 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001378 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1379 {
1380 if (extends != NULL)
1381 {
1382 emsg(_(e_duplicate_extends));
1383 goto early_ret;
1384 }
1385 arg = skipwhite(arg + 7);
1386 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1387 if (!IS_WHITE_OR_NUL(*end))
1388 {
1389 semsg(_(e_white_space_required_after_name_str), arg);
1390 goto early_ret;
1391 }
1392 extends = vim_strnsave(arg, end - arg);
1393 if (extends == NULL)
1394 goto early_ret;
1395
1396 arg = skipwhite(end + 1);
1397 }
1398 else if (STRNCMP(arg, "implements", 10) == 0
1399 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001400 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001401 if (!is_class)
1402 {
1403 emsg(_(e_interface_cannot_use_implements));
1404 goto early_ret;
1405 }
1406
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001407 if (ga_impl.ga_len > 0)
1408 {
1409 emsg(_(e_duplicate_implements));
1410 goto early_ret;
1411 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001412 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001413
1414 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001415 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001416 char_u *impl_end = find_name_end(arg, NULL, NULL,
1417 FNE_CHECK_START);
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001418 if ((!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1419 || (*impl_end == ','
1420 && !IS_WHITE_OR_NUL(*(impl_end + 1))))
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001421 {
1422 semsg(_(e_white_space_required_after_name_str), arg);
1423 goto early_ret;
1424 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001425 if (impl_end - arg == 0)
1426 {
1427 emsg(_(e_missing_name_after_implements));
1428 goto early_ret;
1429 }
1430
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001431 char_u *iname = vim_strnsave(arg, impl_end - arg);
1432 if (iname == NULL)
1433 goto early_ret;
1434 for (int i = 0; i < ga_impl.ga_len; ++i)
1435 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1436 {
1437 semsg(_(e_duplicate_interface_after_implements_str),
1438 iname);
1439 vim_free(iname);
1440 goto early_ret;
1441 }
1442 if (ga_add_string(&ga_impl, iname) == FAIL)
1443 {
1444 vim_free(iname);
1445 goto early_ret;
1446 }
1447 if (*impl_end != ',')
1448 {
1449 arg = skipwhite(impl_end);
1450 break;
1451 }
1452 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001453 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001454 }
1455 else
1456 {
1457 semsg(_(e_trailing_characters_str), arg);
1458early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001459 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001460 ga_clear_strings(&ga_impl);
1461 return;
1462 }
1463 }
1464
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001465 garray_T type_list; // list of pointers to allocated types
1466 ga_init2(&type_list, sizeof(type_T *), 10);
1467
Bram Moolenaard505d172022-12-18 21:42:55 +00001468 // Growarray with class members declared in the class.
1469 garray_T classmembers;
1470 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1471
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001472 // Growarray with functions declared in the class.
1473 garray_T classfunctions;
1474 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001475
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001476 // Growarray with object members declared in the class.
1477 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001478 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001479
1480 // Growarray with object methods declared in the class.
1481 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001482 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001483
1484 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001485 * Go over the body of the class/interface until "endclass" or
1486 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001487 */
1488 char_u *theline = NULL;
1489 int success = FALSE;
1490 for (;;)
1491 {
1492 vim_free(theline);
1493 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
1494 if (theline == NULL)
1495 break;
1496 char_u *line = skipwhite(theline);
1497
Bram Moolenaar418b5472022-12-20 13:38:22 +00001498 // Skip empty and comment lines.
1499 if (*line == NUL)
1500 continue;
1501 if (*line == '#')
1502 {
1503 if (vim9_bad_comment(line))
1504 break;
1505 continue;
1506 }
1507
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001508 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001509 char *end_name = is_class ? "endclass" : "endinterface";
1510 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001511 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001512 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001513 semsg(_(e_command_cannot_be_shortened_str), line);
1514 else if (*p == '|' || !ends_excmd2(line, p))
1515 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001516 else
1517 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001518 break;
1519 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001520 char *wrong_name = is_class ? "endinterface" : "endclass";
1521 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1522 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001523 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001524 break;
1525 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001526
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001527 int has_public = FALSE;
1528 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001529 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001530 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001531 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001532 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001533 break;
1534 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001535 if (!is_class)
1536 {
Yegappan Lakshmananb90e3bc2023-09-28 23:06:48 +02001537 emsg(_(e_public_variable_not_supported_in_interface));
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001538 break;
1539 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001540 has_public = TRUE;
1541 p = skipwhite(line + 6);
1542
Bram Moolenaard505d172022-12-18 21:42:55 +00001543 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001544 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001545 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001546 break;
1547 }
1548 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001549
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001550 int abstract_method = FALSE;
1551 char_u *pa = p;
1552 if (checkforcmd(&p, "abstract", 3))
1553 {
1554 if (STRNCMP(pa, "abstract", 8) != 0)
1555 {
1556 semsg(_(e_command_cannot_be_shortened_str), pa);
1557 break;
1558 }
1559
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001560 if (!is_class)
1561 // ignore "abstract" in an interface (as all the methods in an
1562 // interface are abstract.
1563 p = skipwhite(pa + 8);
1564 else
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001565 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001566 if (!is_abstract)
1567 {
1568 semsg(_(e_abstract_method_in_concrete_class), pa);
1569 break;
1570 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001571
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001572 abstract_method = TRUE;
1573 p = skipwhite(pa + 8);
1574 if (STRNCMP(p, "def", 3) != 0 && STRNCMP(p, "static", 6) != 0)
1575 {
1576 emsg(_(e_abstract_must_be_followed_by_def_or_static));
1577 break;
1578 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001579 }
1580 }
1581
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001582 int has_static = FALSE;
1583 char_u *ps = p;
1584 if (checkforcmd(&p, "static", 4))
1585 {
1586 if (STRNCMP(ps, "static", 6) != 0)
1587 {
1588 semsg(_(e_command_cannot_be_shortened_str), ps);
1589 break;
1590 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001591
1592 if (!is_class)
1593 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001594 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001595 break;
1596 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001597 has_static = TRUE;
1598 p = skipwhite(ps + 6);
1599 }
1600
Bram Moolenaard505d172022-12-18 21:42:55 +00001601 // object members (public, read access, private):
1602 // "this._varname"
1603 // "this.varname"
1604 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001605 if (STRNCMP(p, "this", 4) == 0)
1606 {
1607 if (p[4] != '.' || !eval_isnamec1(p[5]))
1608 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001609 semsg(_(e_invalid_object_variable_declaration_str), p);
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001610 break;
1611 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001612 if (has_static)
1613 {
1614 emsg(_(e_static_cannot_be_followed_by_this));
1615 break;
1616 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001617 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +00001618 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001619 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001620 char_u *init_expr = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001621 int has_type = FALSE;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001622
1623 if (!is_class && *varname == '_')
1624 {
1625 // private variables are not supported in an interface
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001626 semsg(_(e_private_variable_not_supported_in_interface),
1627 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001628 break;
1629 }
1630
Bram Moolenaard505d172022-12-18 21:42:55 +00001631 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001632 &varname_end, &has_type, &type_list, &type,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001633 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001634 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001635 if (is_reserved_varname(varname, varname_end))
1636 {
1637 vim_free(init_expr);
1638 break;
1639 }
1640 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1641 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001642 {
1643 vim_free(init_expr);
1644 break;
1645 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001646 if (add_member(&objmembers, varname, varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001647 has_public, has_type, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001648 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001649 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001650 break;
1651 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001652 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001653
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001654 // constructors:
1655 // def new()
1656 // enddef
1657 // def newOther()
1658 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001659 // object methods and class functions:
1660 // def SomeMethod()
1661 // enddef
1662 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001663 // enddef
1664 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001665 // def <Tval> someMethod()
1666 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001667 else if (checkforcmd(&p, "def", 3))
1668 {
1669 exarg_T ea;
1670 garray_T lines_to_free;
1671
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001672 if (has_public)
1673 {
1674 // "public" keyword is not supported when defining an object or
1675 // class method
1676 emsg(_(e_public_keyword_not_supported_for_method));
1677 break;
1678 }
1679
1680 if (*p == NUL)
1681 {
1682 // No method name following def
1683 semsg(_(e_not_valid_command_in_class_str), line);
1684 break;
1685 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001686
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001687 CLEAR_FIELD(ea);
1688 ea.cmd = line;
1689 ea.arg = p;
1690 ea.cmdidx = CMD_def;
1691 ea.getline = eap->getline;
1692 ea.cookie = eap->cookie;
1693
1694 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001695 int class_flags;
1696 if (is_class)
1697 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
1698 else
1699 class_flags = CF_INTERFACE;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001700 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02001701 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001702 ga_clear_strings(&lines_to_free);
1703
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001704 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001705 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001706 char_u *name = uf->uf_name;
1707 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001708
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001709 if (!is_class && *name == '_')
1710 {
1711 // private variables are not supported in an interface
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001712 semsg(_(e_private_method_not_supported_in_interface),
1713 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001714 func_clear_free(uf, FALSE);
1715 break;
1716 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001717 if (is_new && !is_valid_constructor(uf, is_abstract,
1718 has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001719 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001720 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001721 break;
1722 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001723
Bram Moolenaar58b40092023-01-11 15:59:05 +00001724 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001725 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001726 {
1727 success = FALSE;
1728 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001729 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001730 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001731
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001732 garray_T *fgap = has_static || is_new
1733 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001734 if (ga_grow(fgap, 1) == OK)
1735 {
1736 if (is_new)
1737 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001738
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001739 if (abstract_method)
1740 uf->uf_flags |= FC_ABSTRACT;
1741
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001742 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1743 ++fgap->ga_len;
1744 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001745 }
1746 }
1747
1748 // class members
1749 else if (has_static)
1750 {
1751 // class members (public, read access, private):
1752 // "static _varname"
1753 // "static varname"
1754 // "public static varname"
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001755 char_u *varname = p;
1756 char_u *varname_end = NULL;
1757 int has_type = FALSE;
1758 type_T *type = NULL;
1759 char_u *init_expr = NULL;
1760
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001761 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001762 &varname_end, &has_type, &type_list, &type,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001763 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001764 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001765 if (is_reserved_varname(varname, varname_end))
1766 {
1767 vim_free(init_expr);
1768 break;
1769 }
1770 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1771 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001772 {
1773 vim_free(init_expr);
1774 break;
1775 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001776 if (add_member(&classmembers, varname, varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001777 has_public, has_type, type, init_expr) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001778 {
1779 vim_free(init_expr);
1780 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001781 }
1782 }
1783
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001784 else
1785 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001786 if (is_class)
1787 semsg(_(e_not_valid_command_in_class_str), line);
1788 else
1789 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001790 break;
1791 }
1792 }
1793 vim_free(theline);
1794
Bram Moolenaar83677162023-01-08 19:54:10 +00001795 class_T *extends_cl = NULL; // class from "extends" argument
1796
1797 /*
1798 * Check a few things before defining the class.
1799 */
1800
1801 // Check the "extends" class is valid.
1802 if (success && extends != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001803 success = validate_extends_class(extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00001804 VIM_CLEAR(extends);
1805
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001806 // Check the new object methods to make sure their access (public or
1807 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001808 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001809 success = validate_extends_methods(&objmethods, extends_cl);
1810
1811 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001812 // variables in the extended class lineage. If an interface is extending
1813 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001814 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001815 {
1816 if (is_class)
1817 success = extends_check_dup_members(&objmembers, extends_cl);
1818 else
1819 success = extends_check_intf_var_type(&objmembers, extends_cl);
1820 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001821
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001822 // When extending an abstract class, make sure all the abstract methods in
1823 // the parent class are implemented. If the current class is an abstract
1824 // class, then there is no need for this check.
1825 if (success && !is_abstract && extends_cl != NULL
1826 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001827 success = validate_abstract_class_methods(&classfunctions,
1828 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001829
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001830 class_T **intf_classes = NULL;
1831
Bram Moolenaar83677162023-01-08 19:54:10 +00001832 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001833 if (success && ga_impl.ga_len > 0)
1834 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001835 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1836
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001837 success = validate_implements_classes(&ga_impl, intf_classes,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02001838 &objmethods, &objmembers, extends_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001839 }
1840
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001841 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001842 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001843 success = check_func_arg_names(&classfunctions, &objmethods,
1844 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001845
Bram Moolenaareb533502022-12-14 15:06:11 +00001846 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001847 if (success)
1848 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001849 // "endclass" encountered without failures: Create the class.
1850
Bram Moolenaareb533502022-12-14 15:06:11 +00001851 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001852 if (cl == NULL)
1853 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001854 if (!is_class)
1855 cl->class_flags = CLASS_INTERFACE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001856 else if (is_abstract)
1857 cl->class_flags = CLASS_ABSTRACT;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001858
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001859 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001860 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001861 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001862 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001863
Bram Moolenaard0200c82023-01-28 15:19:40 +00001864 if (extends_cl != NULL)
1865 {
1866 cl->class_extends = extends_cl;
1867 extends_cl->class_flags |= CLASS_EXTENDED;
1868 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001869
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001870 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00001871 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001872 NULL,
1873 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00001874 &cl->class_class_members,
1875 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001876 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001877 extends_cl == NULL ? NULL
1878 : extends_cl->class_obj_members,
1879 extends_cl == NULL ? 0
1880 : extends_cl->class_obj_member_count,
1881 &cl->class_obj_members,
1882 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001883 goto cleanup;
1884
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001885 if (ga_impl.ga_len > 0)
1886 {
1887 // Move the "implements" names into the class.
1888 cl->class_interface_count = ga_impl.ga_len;
1889 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1890 if (cl->class_interfaces == NULL)
1891 goto cleanup;
1892 for (int i = 0; i < ga_impl.ga_len; ++i)
1893 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1894 VIM_CLEAR(ga_impl.ga_data);
1895 ga_impl.ga_len = 0;
1896
Bram Moolenaard0200c82023-01-28 15:19:40 +00001897 cl->class_interfaces_cl = intf_classes;
1898 intf_classes = NULL;
1899 }
1900
1901 if (cl->class_interface_count > 0 || extends_cl != NULL)
1902 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001903 // Add a method and member lookup table to each of the interface
1904 // classes.
1905 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1906 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001907 }
1908
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001909 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001910 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001911 add_class_members(cl, eap, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001912
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001913 int have_new = FALSE;
1914 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001915 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001916 {
1917 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1918 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001919 {
1920 have_new = TRUE;
1921 break;
1922 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001923 }
1924
1925 if (have_new)
1926 // The return type of new() is an object of class "cl"
1927 class_func->uf_ret_type->tt_class = cl;
1928 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001929 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001930 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001931
Bram Moolenaar58b40092023-01-11 15:59:05 +00001932 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001933 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1934 &objmethods) == FAIL)
1935 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001936
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001937 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001938 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001939 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001940 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001941 cl->class_type_list = type_list;
1942
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001943 class_created(cl);
1944
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001945 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001946 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001947
1948 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001949 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02001950 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001951 typval_T tv;
1952 tv.v_type = VAR_CLASS;
1953 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001954 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001955 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001956 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001957 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001958 return;
1959 }
1960
1961cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00001962 if (cl != NULL)
1963 {
1964 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001965 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001966 if (cl->class_interfaces != NULL)
1967 {
1968 for (int i = 0; i < cl->class_interface_count; ++i)
1969 vim_free(cl->class_interfaces[i]);
1970 vim_free(cl->class_interfaces);
1971 }
1972 if (cl->class_interfaces_cl != NULL)
1973 {
1974 for (int i = 0; i < cl->class_interface_count; ++i)
1975 class_unref(cl->class_interfaces_cl[i]);
1976 vim_free(cl->class_interfaces_cl);
1977 }
Bram Moolenaareb533502022-12-14 15:06:11 +00001978 vim_free(cl->class_obj_members);
1979 vim_free(cl->class_obj_methods);
1980 vim_free(cl);
1981 }
1982
Bram Moolenaar83677162023-01-08 19:54:10 +00001983 vim_free(extends);
1984 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001985
1986 if (intf_classes != NULL)
1987 {
1988 for (int i = 0; i < ga_impl.ga_len; ++i)
1989 class_unref(intf_classes[i]);
1990 vim_free(intf_classes);
1991 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001992 ga_clear_strings(&ga_impl);
1993
Bram Moolenaard505d172022-12-18 21:42:55 +00001994 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001995 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001996 garray_T *gap = round == 1 ? &classmembers : &objmembers;
1997 if (gap->ga_len == 0 || gap->ga_data == NULL)
1998 continue;
1999
2000 for (int i = 0; i < gap->ga_len; ++i)
2001 {
2002 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2003 vim_free(m->ocm_name);
2004 vim_free(m->ocm_init);
2005 }
2006 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002007 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002008
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002009 for (int i = 0; i < objmethods.ga_len; ++i)
2010 {
2011 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2012 func_clear_free(uf, FALSE);
2013 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002014 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002015
2016 for (int i = 0; i < classfunctions.ga_len; ++i)
2017 {
2018 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2019 func_clear_free(uf, FALSE);
2020 }
2021 ga_clear(&classfunctions);
2022
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002023 clear_type_list(&type_list);
2024}
2025
2026/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002027 * Find member "name" in class "cl", set "member_idx" to the member index and
2028 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002029 * When "is_object" is TRUE, then look for object members. Otherwise look for
2030 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002031 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002032 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002033 */
2034 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002035oc_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002036 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002037 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002038 char_u *name,
2039 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002040 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002041{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002042 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002043 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002044
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002045 *member_idx = -1; // not found (yet)
2046
2047 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2048 member_idx);
2049 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002050 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002051 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2052 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002053 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002054 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002055
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002056 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002057}
2058
2059/*
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002060 * Given a class or object variable index, return the variable type
2061 */
2062 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002063oc_member_type_by_idx(
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002064 class_T *cl,
2065 int is_object,
2066 int member_idx)
2067{
2068 ocmember_T *m;
2069 int member_count;
2070
2071 if (is_object)
2072 {
2073 m = cl->class_obj_members;
2074 member_count = cl->class_obj_member_count;
2075 }
2076 else
2077 {
2078 m = cl->class_class_members;
2079 member_count = cl->class_class_member_count;
2080 }
2081
2082 if (member_idx >= member_count)
2083 return NULL;
2084
2085 return m[member_idx].ocm_type;
2086}
2087
2088/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002089 * Handle ":enum" up to ":endenum".
2090 */
2091 void
2092ex_enum(exarg_T *eap UNUSED)
2093{
2094 // TODO
2095}
2096
2097/*
2098 * Handle ":type".
2099 */
2100 void
2101ex_type(exarg_T *eap UNUSED)
2102{
2103 // TODO
2104}
2105
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002106/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002107 * Returns OK if a member variable named "name" is present in the class "cl".
2108 * Otherwise returns FAIL. If found, the member variable typval is set in
2109 * "rettv". If "is_object" is TRUE, then the object member variable table is
2110 * searched. Otherwise the class member variable table is searched.
2111 */
2112 static int
2113get_member_tv(
2114 class_T *cl,
2115 int is_object,
2116 char_u *name,
2117 size_t namelen,
2118 typval_T *rettv)
2119{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002120 ocmember_T *m;
2121 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002122
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002123 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2124 &m_idx);
2125 if (m == NULL)
2126 return FAIL;
2127
2128 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002129 {
Ernie Raele6c9aa52023-10-06 19:55:52 +02002130 emsg_var_cl_define(e_cannot_access_private_variable_str,
2131 m->ocm_name, 0, cl);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002132 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002133 }
2134
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002135 if (is_object)
2136 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002137 // The object only contains a pointer to the class, the member values
2138 // array follows right after that.
2139 object_T *obj = rettv->vval.v_object;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002140 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2141 copy_tv(tv, rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002142 object_unref(obj);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002143 }
2144 else
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002145 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002146 copy_tv(&cl->class_members_tv[m_idx], rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002147 class_unref(cl);
2148 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002149
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002150 return OK;
2151}
2152
2153/*
2154 * Call an object or class method "name" in class "cl". The method return
2155 * value is returned in "rettv".
2156 */
2157 static int
2158call_oc_method(
2159 class_T *cl,
2160 char_u *name,
2161 size_t len,
2162 char_u *name_end,
2163 evalarg_T *evalarg,
2164 char_u **arg,
2165 typval_T *rettv)
2166{
2167 ufunc_T *fp;
2168 typval_T argvars[MAX_FUNC_ARGS + 1];
2169 int argcount = 0;
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002170 ocmember_T *ocm = NULL;
2171 int m_idx;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002172
2173 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
2174 if (fp == NULL)
2175 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002176 // could be an object or class funcref variable
2177 ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
2178 if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
2179 {
2180 method_not_found_msg(cl, rettv->v_type, name, len);
2181 return FAIL;
2182 }
2183
2184 if (rettv->v_type == VAR_OBJECT)
2185 {
2186 // funcref object variable
2187 object_T *obj = rettv->vval.v_object;
2188 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2189 copy_tv(tv, rettv);
2190 }
2191 else
2192 // funcref class variable
2193 copy_tv(&cl->class_members_tv[m_idx], rettv);
2194 *arg = name_end;
2195 return OK;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002196 }
2197
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002198 if (ocm == NULL && *fp->uf_name == '_')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002199 {
2200 // Cannot access a private method outside of a class
2201 semsg(_(e_cannot_access_private_method_str), fp->uf_name);
2202 return FAIL;
2203 }
2204
2205 char_u *argp = name_end;
2206 int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount);
2207 if (ret == FAIL)
2208 return FAIL;
2209
2210 funcexe_T funcexe;
2211 CLEAR_FIELD(funcexe);
2212 funcexe.fe_evaluate = TRUE;
2213 if (rettv->v_type == VAR_OBJECT)
2214 {
2215 funcexe.fe_object = rettv->vval.v_object;
2216 ++funcexe.fe_object->obj_refcount;
2217 }
2218
2219 // Clear the class or object after calling the function, in
2220 // case the refcount is one.
2221 typval_T tv_tofree = *rettv;
2222 rettv->v_type = VAR_UNKNOWN;
2223
2224 // Call the user function. Result goes into rettv;
2225 int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
2226 NULL);
2227
2228 // Clear the previous rettv and the arguments.
2229 clear_tv(&tv_tofree);
2230 for (int idx = 0; idx < argcount; ++idx)
2231 clear_tv(&argvars[idx]);
2232
2233 if (error != FCERR_NONE)
2234 {
2235 user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
2236 return FAIL;
2237 }
2238 *arg = argp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002239
2240 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002241}
2242
2243/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002244 * Evaluate what comes after a class:
2245 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002246 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002247 * - class constructor: SomeClass.new()
2248 * - object member: someObject.varname
2249 * - object method: someObject.SomeMethod()
2250 *
2251 * "*arg" points to the '.'.
2252 * "*arg" is advanced to after the member name or method call.
2253 *
2254 * Returns FAIL or OK.
2255 */
2256 int
2257class_object_index(
2258 char_u **arg,
2259 typval_T *rettv,
2260 evalarg_T *evalarg,
2261 int verbose UNUSED) // give error messages
2262{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002263 if (VIM_ISWHITE((*arg)[1]))
2264 {
2265 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
2266 return FAIL;
2267 }
2268
2269 ++*arg;
2270 char_u *name = *arg;
2271 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2272 if (name_end == name)
2273 return FAIL;
2274 size_t len = name_end - name;
2275
Ernie Raeld615a312023-10-05 20:28:16 +02002276 int did_emsg_save = did_emsg;
Bram Moolenaar552bdca2023-02-17 21:08:50 +00002277 class_T *cl;
2278 if (rettv->v_type == VAR_CLASS)
2279 cl = rettv->vval.v_class;
2280 else // VAR_OBJECT
2281 {
2282 if (rettv->vval.v_object == NULL)
2283 {
2284 emsg(_(e_using_null_object));
2285 return FAIL;
2286 }
2287 cl = rettv->vval.v_object->obj_class;
2288 }
2289
Bram Moolenaard13dd302023-03-11 20:56:35 +00002290 if (cl == NULL)
2291 {
2292 emsg(_(e_incomplete_type));
2293 return FAIL;
2294 }
2295
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002296 if (*name_end == '(')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002297 // Invoke the class or object method
2298 return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002299
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002300 else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002301 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002302 // Search in the object member variable table and the class member
2303 // variable table.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002304 int is_object = rettv->v_type == VAR_OBJECT;
2305 if (get_member_tv(cl, is_object, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002306 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002307 *arg = name_end;
2308 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002309 }
2310
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002311 // could be a class method or an object method
2312 int fidx;
2313 ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
2314 if (fp != NULL)
2315 {
2316 // Private methods are not accessible outside the class
2317 if (*name == '_')
2318 {
2319 semsg(_(e_cannot_access_private_method_str), fp->uf_name);
2320 return FAIL;
2321 }
2322
2323 partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
2324 if (pt == NULL)
2325 return FAIL;
2326
2327 pt->pt_refcount = 1;
2328 if (is_object)
2329 {
2330 pt->pt_obj = rettv->vval.v_object;
2331 ++pt->pt_obj->obj_refcount;
2332 }
2333 pt->pt_auto = TRUE;
2334 pt->pt_func = fp;
2335 func_ptr_ref(pt->pt_func);
2336 rettv->v_type = VAR_PARTIAL;
2337 rettv->vval.v_partial = pt;
2338 *arg = name_end;
2339 return OK;
2340 }
2341
Ernie Raeld615a312023-10-05 20:28:16 +02002342 if (did_emsg == did_emsg_save)
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +02002343 member_not_found_msg(cl, rettv->v_type, name, len);
Bram Moolenaard505d172022-12-18 21:42:55 +00002344 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002345
2346 return FAIL;
2347}
2348
2349/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002350 * If "arg" points to a class or object method, return it.
2351 * Otherwise return NULL.
2352 */
2353 ufunc_T *
2354find_class_func(char_u **arg)
2355{
2356 char_u *name = *arg;
2357 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2358 if (name_end == name || *name_end != '.')
2359 return NULL;
2360
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002361 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002362 size_t len = name_end - name;
2363 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002364 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00002365 if (eval_variable(name, (int)len,
2366 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002367 return NULL;
2368 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00002369 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002370
2371 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
2372 : tv.vval.v_object->obj_class;
2373 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002374 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002375 char_u *fname = name_end + 1;
2376 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
2377 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00002378 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002379 len = fname_end - fname;
2380
Ernie Rael4d00b832023-09-11 19:54:42 +02002381 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002382
Bram Moolenaareb533502022-12-14 15:06:11 +00002383fail_after_eval:
2384 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002385 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002386}
2387
2388/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002389 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002390 * Returns -1, if the variable is not found.
2391 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002392 */
2393 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002394class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002395{
Ernie Rael4d00b832023-09-11 19:54:42 +02002396 int idx;
2397 class_member_lookup(cl, name, namelen, &idx);
2398 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002399}
2400
2401/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002402 * Returns a pointer to the class member variable "name" in the class "cl".
2403 * Returns NULL if the variable is not found.
2404 * The member variable index is set in "idx".
2405 */
2406 ocmember_T *
2407class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2408{
Ernie Rael4d00b832023-09-11 19:54:42 +02002409 ocmember_T *ret_m = NULL;
2410 int ret_idx = -1;
2411 for (int i = 0; i < cl->class_class_member_count; ++i)
2412 {
2413 ocmember_T *m = &cl->class_class_members[i];
2414 if (namelen)
2415 {
2416 if (STRNCMP(name, m->ocm_name, namelen) == 0
2417 && m->ocm_name[namelen] == NUL)
2418 {
2419 ret_m = m;
2420 ret_idx = i;
2421 break;
2422 }
2423 }
2424 else if (STRCMP(name, m->ocm_name) == 0)
2425 {
2426 ret_m = m;
2427 ret_idx = i;
2428 break;
2429 }
2430 }
2431 if (idx != NULL)
2432 *idx = ret_idx;
2433 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002434}
2435
2436/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002437 * Returns a pointer to the class method "name" in class "cl".
2438 * Returns NULL if the method is not found.
2439 * The method index is set in "idx".
2440 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002441 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002442class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2443{
Ernie Rael4d00b832023-09-11 19:54:42 +02002444 ufunc_T *ret_fp = NULL;
2445 int ret_idx = -1;
2446 for (int i = 0; i < cl->class_class_function_count; ++i)
2447 {
2448 ufunc_T *fp = cl->class_class_functions[i];
2449 char_u *ufname = (char_u *)fp->uf_name;
2450 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2451 {
2452 ret_fp = fp;
2453 ret_idx = i;
2454 break;
2455 }
2456 }
2457 if (idx != NULL)
2458 *idx = ret_idx;
2459 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002460}
2461
2462/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002463 * Returns the index of class method "name" in the class "cl".
2464 * Returns -1, if the method is not found.
2465 */
2466 int
2467class_method_idx(class_T *cl, char_u *name, size_t namelen)
2468{
2469 int idx;
2470 class_method_lookup(cl, name, namelen, &idx);
2471 return idx;
2472}
2473
2474/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002475 * Returns the index of object member variable "name" in the class "cl".
2476 * Returns -1, if the variable is not found.
2477 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
2478 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002479 static int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002480object_member_idx(class_T *cl, char_u *name, size_t namelen)
2481{
Ernie Rael4d00b832023-09-11 19:54:42 +02002482 int idx;
2483 object_member_lookup(cl, name, namelen, &idx);
2484 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002485}
2486
2487/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002488 * Returns a pointer to the object member variable "name" in the class "cl".
2489 * Returns NULL if the variable is not found.
2490 * The object member variable index is set in "idx".
2491 */
2492 ocmember_T *
2493object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2494{
Ernie Rael4d00b832023-09-11 19:54:42 +02002495 ocmember_T *ret_m = NULL;
2496 int ret_idx = -1;
2497 for (int i = 0; i < cl->class_obj_member_count; ++i)
2498 {
2499 ocmember_T *m = &cl->class_obj_members[i];
2500 if (namelen)
2501 {
2502 if (STRNCMP(name, m->ocm_name, namelen) == 0
2503 && m->ocm_name[namelen] == NUL)
2504 {
2505 ret_m = m;
2506 ret_idx = i;
2507 break;
2508 }
2509 }
2510 else if (STRCMP(name, m->ocm_name) == 0)
2511 {
2512 ret_m = m;
2513 ret_idx = i;
2514 break;
2515 }
2516 }
2517 if (idx != NULL)
2518 *idx = ret_idx;
2519 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002520}
2521
2522/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002523 * Returns a pointer to the object method "name" in class "cl".
2524 * Returns NULL if the method is not found.
2525 * The object method index is set in "idx".
2526 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002527 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002528object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2529{
Ernie Rael4d00b832023-09-11 19:54:42 +02002530 ufunc_T *ret_fp = NULL;
2531 int ret_idx = -1;
2532 for (int i = 0; i < cl->class_obj_method_count; ++i)
2533 {
2534 ufunc_T *fp = cl->class_obj_methods[i];
2535 // Use a separate pointer to avoid that ASAN complains about
2536 // uf_name[] only being 4 characters.
2537 char_u *ufname = (char_u *)fp->uf_name;
2538 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2539 {
2540 ret_fp = fp;
2541 ret_idx = i;
2542 break;
2543 }
2544 }
2545 if (idx != NULL)
2546 *idx = ret_idx;
2547 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002548}
2549
2550/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002551 * Returns the index of object method "name" in the class "cl".
2552 * Returns -1, if the method is not found.
2553 */
2554 int
2555object_method_idx(class_T *cl, char_u *name, size_t namelen)
2556{
2557 int idx;
2558 object_method_lookup(cl, name, namelen, &idx);
2559 return idx;
2560}
2561
2562/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002563 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
2564 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
2565 * object member variable.
2566 *
2567 * Returns a pointer to the member variable structure if variable is found.
2568 * Otherwise returns NULL. The member variable index is set in "*idx".
2569 */
2570 ocmember_T *
2571member_lookup(
2572 class_T *cl,
2573 vartype_T v_type,
2574 char_u *name,
2575 size_t namelen,
2576 int *idx)
2577{
2578 if (v_type == VAR_CLASS)
2579 return class_member_lookup(cl, name, namelen, idx);
2580 else
2581 return object_member_lookup(cl, name, namelen, idx);
2582}
2583
2584/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02002585 * Find the class that defines the named member. Look up the hierarchy
2586 * starting at "cl".
2587 *
2588 * Return the class that defines the member "name", else NULL.
2589 * Fill in "p_m", if specified, for ocmember_T in found class.
2590 */
2591// NOTE: if useful for something could also indirectly return vartype and idx.
2592 static class_T *
2593class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m)
2594{
2595 class_T *cl_found = NULL;
2596 vartype_T vartype = VAR_UNKNOWN;
2597 ocmember_T *m_found = NULL;
2598
2599 len = len != 0 ? len : STRLEN(name);
2600
2601 // Loop assumes if member is not defined in "cl", then it is not
2602 // defined in any super class; the last class where it's found is the
2603 // class where it is defined. Once the vartype is found, the other
2604 // type is no longer checked.
2605 for (class_T *super = cl; super != NULL; super = super->class_extends)
2606 {
2607 class_T *cl_tmp = NULL;
2608 ocmember_T *m = NULL;
2609 if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT)
2610 {
2611 if ((m = object_member_lookup(super, name, len, NULL)) != NULL)
2612 {
2613 cl_tmp = super;
2614 vartype = VAR_OBJECT;
2615 }
2616 }
2617 if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS)
2618 {
2619 if (( m = class_member_lookup(super, name, len, NULL)) != NULL)
2620 {
2621 cl_tmp = super;
2622 vartype = VAR_OBJECT;
2623 }
2624 }
2625 if (cl_tmp == NULL)
2626 break; // member is not in this or any super class.
2627 cl_found = cl_tmp;
2628 m_found = m;
2629 }
2630 if (p_m != NULL)
2631 *p_m = m_found;
2632 return cl_found;
2633}
2634
2635/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002636 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
2637 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
2638 *
2639 * Returns a pointer to the method structure if variable is found.
2640 * Otherwise returns NULL. The method variable index is set in "*idx".
2641 */
2642 ufunc_T *
2643method_lookup(
2644 class_T *cl,
2645 vartype_T v_type,
2646 char_u *name,
2647 size_t namelen,
2648 int *idx)
2649{
2650 if (v_type == VAR_CLASS)
2651 return class_method_lookup(cl, name, namelen, idx);
2652 else
2653 return object_method_lookup(cl, name, namelen, idx);
2654}
2655
2656/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00002657 * Return TRUE if current context "cctx_arg" is inside class "cl".
2658 * Return FALSE if not.
2659 */
2660 int
2661inside_class(cctx_T *cctx_arg, class_T *cl)
2662{
2663 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002664 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002665 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002666 return TRUE;
2667 return FALSE;
2668}
2669
2670/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002671 * Make a copy of an object.
2672 */
2673 void
2674copy_object(typval_T *from, typval_T *to)
2675{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002676 if (from->vval.v_object == NULL)
2677 to->vval.v_object = NULL;
2678 else
2679 {
2680 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002681 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002682 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002683}
2684
2685/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002686 * Make a copy of a class.
2687 */
2688 void
2689copy_class(typval_T *from, typval_T *to)
2690{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002691 if (from->vval.v_class == NULL)
2692 to->vval.v_class = NULL;
2693 else
2694 {
2695 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002696 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002697 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002698}
2699
2700/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002701 * Free the class "cl" and its contents.
2702 */
2703 static void
2704class_free(class_T *cl)
2705{
2706 // Freeing what the class contains may recursively come back here.
2707 // Clear "class_name" first, if it is NULL the class does not need to
2708 // be freed.
2709 VIM_CLEAR(cl->class_name);
2710
2711 class_unref(cl->class_extends);
2712
2713 for (int i = 0; i < cl->class_interface_count; ++i)
2714 {
2715 vim_free(((char_u **)cl->class_interfaces)[i]);
2716 if (cl->class_interfaces_cl[i] != NULL)
2717 class_unref(cl->class_interfaces_cl[i]);
2718 }
2719 vim_free(cl->class_interfaces);
2720 vim_free(cl->class_interfaces_cl);
2721
2722 itf2class_T *next;
2723 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2724 {
2725 next = i2c->i2c_next;
2726 vim_free(i2c);
2727 }
2728
2729 for (int i = 0; i < cl->class_class_member_count; ++i)
2730 {
2731 ocmember_T *m = &cl->class_class_members[i];
2732 vim_free(m->ocm_name);
2733 vim_free(m->ocm_init);
2734 if (cl->class_members_tv != NULL)
2735 clear_tv(&cl->class_members_tv[i]);
2736 }
2737 vim_free(cl->class_class_members);
2738 vim_free(cl->class_members_tv);
2739
2740 for (int i = 0; i < cl->class_obj_member_count; ++i)
2741 {
2742 ocmember_T *m = &cl->class_obj_members[i];
2743 vim_free(m->ocm_name);
2744 vim_free(m->ocm_init);
2745 }
2746 vim_free(cl->class_obj_members);
2747
2748 for (int i = 0; i < cl->class_class_function_count; ++i)
2749 {
2750 ufunc_T *uf = cl->class_class_functions[i];
2751 func_clear_free(uf, FALSE);
2752 }
2753 vim_free(cl->class_class_functions);
2754
2755 for (int i = 0; i < cl->class_obj_method_count; ++i)
2756 {
2757 ufunc_T *uf = cl->class_obj_methods[i];
2758 func_clear_free(uf, FALSE);
2759 }
2760 vim_free(cl->class_obj_methods);
2761
2762 clear_type_list(&cl->class_type_list);
2763
2764 class_cleared(cl);
2765
2766 vim_free(cl);
2767}
2768
2769/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002770 * Unreference a class. Free it when the reference count goes down to zero.
2771 */
2772 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002773class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002774{
Bram Moolenaard505d172022-12-18 21:42:55 +00002775 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002776 class_free(cl);
2777}
2778
2779/*
2780 * Go through the list of all classes and free items without "copyID".
2781 */
2782 int
2783class_free_nonref(int copyID)
2784{
2785 int did_free = FALSE;
2786
2787 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002788 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002789 next_nonref_class = cl->class_next_used;
2790 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002791 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002792 // Free the class and items it contains.
2793 class_free(cl);
2794 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002795 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002796 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002797
2798 next_nonref_class = NULL;
2799 return did_free;
2800}
2801
2802 int
2803set_ref_in_classes(int copyID)
2804{
2805 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
2806 set_ref_in_item_class(cl, copyID, NULL, NULL);
2807
2808 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002809}
2810
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002811static object_T *first_object = NULL;
2812
2813/*
2814 * Call this function when an object has been created. It will be added to the
2815 * list headed by "first_object".
2816 */
2817 void
2818object_created(object_T *obj)
2819{
2820 if (first_object != NULL)
2821 {
2822 obj->obj_next_used = first_object;
2823 first_object->obj_prev_used = obj;
2824 }
2825 first_object = obj;
2826}
2827
2828/*
2829 * Call this function when an object has been cleared and is about to be freed.
2830 * It is removed from the list headed by "first_object".
2831 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002832 static void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002833object_cleared(object_T *obj)
2834{
2835 if (obj->obj_next_used != NULL)
2836 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
2837 if (obj->obj_prev_used != NULL)
2838 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
2839 else if (first_object == obj)
2840 first_object = obj->obj_next_used;
2841}
2842
2843/*
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002844 * Free the contents of an object ignoring the reference count.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002845 */
2846 static void
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002847object_free_contents(object_T *obj)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002848{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002849 class_T *cl = obj->obj_class;
2850
2851 if (!cl)
2852 return;
2853
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002854 // Avoid a recursive call, it can happen if "obj" has a circular reference.
2855 obj->obj_refcount = INT_MAX;
2856
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002857 // the member values are just after the object structure
2858 typval_T *tv = (typval_T *)(obj + 1);
2859 for (int i = 0; i < cl->class_obj_member_count; ++i)
2860 clear_tv(tv + i);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002861}
2862
2863 static void
2864object_free_object(object_T *obj)
2865{
2866 class_T *cl = obj->obj_class;
2867
2868 if (!cl)
2869 return;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002870
2871 // Remove from the list headed by "first_object".
2872 object_cleared(obj);
2873
2874 vim_free(obj);
2875 class_unref(cl);
2876}
2877
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002878 static void
2879object_free(object_T *obj)
2880{
2881 if (in_free_unref_items)
2882 return;
2883
2884 object_free_contents(obj);
2885 object_free_object(obj);
2886}
2887
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002888/*
2889 * Unreference an object.
2890 */
2891 void
2892object_unref(object_T *obj)
2893{
2894 if (obj != NULL && --obj->obj_refcount <= 0)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002895 object_free(obj);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002896}
2897
2898/*
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002899 * Go through the list of all objects and free items without "copyID".
2900 */
2901 int
2902object_free_nonref(int copyID)
2903{
2904 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002905
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002906 for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002907 {
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002908 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
2909 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002910 // Free the object contents. Object itself will be freed later.
2911 object_free_contents(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002912 did_free = TRUE;
2913 }
2914 }
2915
2916 return did_free;
2917}
2918
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002919 void
2920object_free_items(int copyID)
2921{
2922 object_T *obj_next;
2923
2924 for (object_T *obj = first_object; obj != NULL; obj = obj_next)
2925 {
2926 obj_next = obj->obj_next_used;
2927 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
2928 object_free_object(obj);
2929 }
2930}
2931
LemonBoyafe04662023-08-23 21:08:11 +02002932/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02002933 * Output message which takes a variable name and the class that defines it.
2934 * "cl" is that class where the name was found. Search "cl"'s hierarchy to
2935 * find the defining class.
2936 */
2937 void
2938emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl)
2939{
2940 ocmember_T *m;
2941 class_T *cl_def = class_defining_member(cl, name, len, &m);
2942 if (cl_def != NULL)
2943 semsg(_(msg), m->ocm_name, cl_def->class_name);
2944 else
2945 emsg(_(e_internal_error_please_report_a_bug));
2946}
2947
2948/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002949 * Echo a class or object method not found message.
2950 */
2951 void
2952method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
2953{
2954 char_u *method_name = vim_strnsave(name, len);
2955 if ((v_type == VAR_OBJECT)
2956 && (class_method_idx(cl, name, len) >= 0))
2957 {
2958 // If this is a class method, then give a different error
2959 if (*name == '_')
2960 semsg(_(e_cannot_access_private_method_str), method_name);
2961 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002962 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002963 method_name, cl->class_name);
2964 }
2965 else if ((v_type == VAR_CLASS)
2966 && (object_method_idx(cl, name, len) >= 0))
2967 {
2968 // If this is an object method, then give a different error
2969 if (*name == '_')
2970 semsg(_(e_cannot_access_private_method_str), method_name);
2971 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002972 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002973 method_name, cl->class_name);
2974 }
2975 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02002976 semsg(_(e_method_not_found_on_class_str_str), method_name,
2977 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002978 vim_free(method_name);
2979}
2980
2981/*
2982 * Echo a class or object member not found message.
2983 */
2984 void
2985member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
2986{
2987 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
2988
2989 if (v_type == VAR_OBJECT)
2990 {
2991 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02002992 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002993 varname, cl->class_name);
2994 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02002995 semsg(_(e_variable_not_found_on_object_str_str), varname,
2996 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002997 }
2998 else
2999 {
3000 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003001 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003002 varname, cl->class_name);
3003 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003004 semsg(_(e_class_variable_str_not_found_in_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003005 varname, cl->class_name);
3006 }
3007 vim_free(varname);
3008}
3009
3010/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02003011 * Return TRUE when the class "cl", its base class or one of the implemented
3012 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02003013 */
3014 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003015class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02003016{
3017 if (cl == other_cl)
3018 return TRUE;
3019
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003020 // Recursively check the base classes.
3021 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02003022 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003023 if (cl == other_cl)
3024 return TRUE;
3025 // Check the implemented interfaces and the super interfaces
3026 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003027 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003028 class_T *intf = cl->class_interfaces_cl[i];
3029 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003030 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003031 if (intf == other_cl)
3032 return TRUE;
3033 // check the super interfaces
3034 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003035 }
3036 }
LemonBoyafe04662023-08-23 21:08:11 +02003037 }
3038
3039 return FALSE;
3040}
3041
3042/*
3043 * "instanceof(object, classinfo)" function
3044 */
3045 void
3046f_instanceof(typval_T *argvars, typval_T *rettv)
3047{
3048 typval_T *object_tv = &argvars[0];
3049 typval_T *classinfo_tv = &argvars[1];
3050 listitem_T *li;
3051
3052 rettv->vval.v_number = VVAL_FALSE;
3053
3054 if (check_for_object_arg(argvars, 0) == FAIL
3055 || check_for_class_or_list_arg(argvars, 1) == FAIL)
3056 return;
3057
Ernie Rael3da696d2023-09-19 20:14:18 +02003058 if (object_tv->vval.v_object == NULL)
3059 return;
3060
LemonBoyafe04662023-08-23 21:08:11 +02003061 if (classinfo_tv->v_type == VAR_LIST)
3062 {
3063 FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
3064 {
3065 if (li->li_tv.v_type != VAR_CLASS)
3066 {
3067 emsg(_(e_class_required));
3068 return;
3069 }
3070
3071 if (class_instance_of(object_tv->vval.v_object->obj_class,
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003072 li->li_tv.vval.v_class) == TRUE)
LemonBoyafe04662023-08-23 21:08:11 +02003073 {
3074 rettv->vval.v_number = VVAL_TRUE;
3075 return;
3076 }
3077 }
3078 }
3079 else if (classinfo_tv->v_type == VAR_CLASS)
3080 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003081 rettv->vval.v_number = class_instance_of(object_tv->vval.v_object->obj_class,
3082 classinfo_tv->vval.v_class);
LemonBoyafe04662023-08-23 21:08:11 +02003083 }
3084}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00003085
3086#endif // FEAT_EVAL