blob: 4c70f8d0fcf6e4de9b06024a59af505c53381f8d [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 Lakshmanan3775f772023-09-01 22:05:45 +020075 garray_T *type_list,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020076 type_T **type_ret,
77 char_u **init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +000078{
79 *varname_end = to_name_end(varname, FALSE);
80 if (*varname == '_' && has_public)
81 {
RestorerZ7fe8f432023-09-24 23:21:24 +020082 semsg(_(e_public_variable_name_cannot_start_with_underscore_str), line);
Bram Moolenaard505d172022-12-18 21:42:55 +000083 return FAIL;
84 }
85
86 char_u *colon = skipwhite(*varname_end);
87 char_u *type_arg = colon;
88 type_T *type = NULL;
89 if (*colon == ':')
90 {
91 if (VIM_ISWHITE(**varname_end))
92 {
93 semsg(_(e_no_white_space_allowed_before_colon_str), varname);
94 return FAIL;
95 }
96 if (!VIM_ISWHITE(colon[1]))
97 {
98 semsg(_(e_white_space_required_after_str_str), ":", varname);
99 return FAIL;
100 }
101 type_arg = skipwhite(colon + 1);
102 type = parse_type(&type_arg, type_list, TRUE);
103 if (type == NULL)
104 return FAIL;
105 }
106
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200107 char_u *init_arg = skipwhite(type_arg);
108 if (type == NULL && *init_arg != '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000109 {
110 emsg(_(e_type_or_initialization_required));
111 return FAIL;
112 }
113
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200114 if (init_expr == NULL && *init_arg == '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000115 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200116 emsg(_(e_cannot_initialize_variable_in_interface));
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200117 return FAIL;
118 }
119
120 if (*init_arg == '=')
121 {
122 evalarg_T evalarg;
123 char_u *expr_start, *expr_end;
124
125 if (!VIM_ISWHITE(init_arg[-1]) || !VIM_ISWHITE(init_arg[1]))
Bram Moolenaard505d172022-12-18 21:42:55 +0000126 {
127 semsg(_(e_white_space_required_before_and_after_str_at_str),
128 "=", type_arg);
129 return FAIL;
130 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200131 init_arg = skipwhite(init_arg + 1);
Bram Moolenaard505d172022-12-18 21:42:55 +0000132
Bram Moolenaard505d172022-12-18 21:42:55 +0000133 fill_evalarg_from_eap(&evalarg, eap, FALSE);
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200134 (void)skip_expr_concatenate(&init_arg, &expr_start, &expr_end, &evalarg);
Bram Moolenaard505d172022-12-18 21:42:55 +0000135
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +0200136 // No type specified for the member. Set it to "any" and the correct
137 // type will be set when the object is instantiated.
Bram Moolenaard505d172022-12-18 21:42:55 +0000138 if (type == NULL)
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200139 type = &t_any;
Bram Moolenaard505d172022-12-18 21:42:55 +0000140
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200141 *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
142 // Free the memory pointed by expr_start.
Bram Moolenaard505d172022-12-18 21:42:55 +0000143 clear_evalarg(&evalarg, NULL);
144 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200145 else if (!valid_declaration_type(type))
Bram Moolenaard505d172022-12-18 21:42:55 +0000146 return FAIL;
147
148 *type_ret = type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000149 return OK;
150}
151
152/*
153 * Add a member to an object or a class.
154 * Returns OK when successful, "init_expr" will be consumed then.
155 * Returns FAIL otherwise, caller might need to free "init_expr".
156 */
157 static int
158add_member(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200159 garray_T *gap,
160 char_u *varname,
161 char_u *varname_end,
162 int has_public,
163 type_T *type,
164 char_u *init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +0000165{
166 if (ga_grow(gap, 1) == FAIL)
167 return FAIL;
168 ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
169 m->ocm_name = vim_strnsave(varname, varname_end - varname);
=?UTF-8?q?Ola=20S=C3=B6der?=d8742472023-03-05 13:12:32 +0000170 m->ocm_access = has_public ? VIM_ACCESS_ALL
171 : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
Bram Moolenaard505d172022-12-18 21:42:55 +0000172 m->ocm_type = type;
173 if (init_expr != NULL)
174 m->ocm_init = init_expr;
175 ++gap->ga_len;
176 return OK;
177}
178
179/*
180 * Move the class or object members found while parsing a class into the class.
181 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000182 * "parent_members" points to the members in the parent class (if any)
183 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000184 * "members" will be set to the newly allocated array of members and
185 * "member_count" set to the number of members.
186 * Returns OK or FAIL.
187 */
188 static int
189add_members_to_class(
190 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000191 ocmember_T *parent_members,
192 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000193 ocmember_T **members,
194 int *member_count)
195{
Bram Moolenaar83677162023-01-08 19:54:10 +0000196 *member_count = parent_count + gap->ga_len;
197 *members = *member_count == 0 ? NULL
198 : ALLOC_MULT(ocmember_T, *member_count);
199 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000200 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000201 for (int i = 0; i < parent_count; ++i)
202 {
203 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000204 ocmember_T *m = *members + i;
205 *m = parent_members[i];
206 m->ocm_name = vim_strsave(m->ocm_name);
207 if (m->ocm_init != NULL)
208 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000209 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000210 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000211 // new members are moved
212 mch_memmove(*members + parent_count,
213 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000214 VIM_CLEAR(gap->ga_data);
215 return OK;
216}
217
218/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000219 * Convert a member index "idx" of interface "itf" to the member index of class
220 * "cl" implementing that interface.
221 */
222 int
Ernie Rael18143d32023-09-04 22:30:41 +0200223object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl,
224 int is_static)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000225{
Ernie Rael18143d32023-09-04 22:30:41 +0200226 if (idx >= (is_method ? itf->class_obj_method_count
227 : is_static ? itf->class_class_member_count
Bram Moolenaard0200c82023-01-28 15:19:40 +0000228 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000229 {
230 siemsg("index %d out of range for interface %s", idx, itf->class_name);
231 return 0;
232 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200233
234 // If "cl" is the interface or the class that is extended, then the method
235 // index can be used directly and there is no need to search for the method
236 // index in one of the child classes.
237 if (cl == itf)
238 return idx;
239
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200240 itf2class_T *i2c = NULL;
241 int searching = TRUE;
242 int method_offset = 0;
243
Ernie Raelcf138d42023-09-06 20:45:03 +0200244 for (class_T *super = cl; super != NULL && searching;
245 super = super->class_extends)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200246 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200247 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200248 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200249 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
250 {
251 searching = FALSE;
252 break;
253 }
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200254 }
255 if (searching && is_method)
256 // The parent class methods are stored after the current class
257 // methods.
258 method_offset += is_static
259 ? super->class_class_function_count_child
260 : super->class_obj_method_count_child;
261 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000262 if (i2c == NULL)
263 {
264 siemsg("class %s not found on interface %s",
265 cl->class_name, itf->class_name);
266 return 0;
267 }
Ernie Rael18143d32023-09-04 22:30:41 +0200268 if (is_static)
269 {
270 // TODO: Need a table for fast lookup?
271 char_u *name = itf->class_class_members[idx].ocm_name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +0200272 int m_idx = class_member_idx(i2c->i2c_class, name, 0);
273 if (m_idx >= 0)
274 return m_idx;
275
Ernie Rael18143d32023-09-04 22:30:41 +0200276 siemsg("class %s, interface %s, static %s not found",
277 cl->class_name, itf->class_name, name);
278 return 0;
279 }
280 else
281 {
282 // A table follows the i2c for the class
283 int *table = (int *)(i2c + 1);
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200284 // "method_offset" is 0, if method is in the current class. If method
285 // is in a parent class, then it is non-zero.
286 return table[idx] + method_offset;
Ernie Rael18143d32023-09-04 22:30:41 +0200287 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000288}
289
290/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200291 * Check whether a class named "extends_name" is present. If the class is
292 * valid, then "extends_clp" is set with the class pointer.
293 * Returns TRUE if the class name "extends_names" is a valid class.
294 */
295 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200296validate_extends_class(
297 char_u *extends_name,
298 class_T **extends_clp,
299 int is_class)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200300{
301 typval_T tv;
302 int success = FALSE;
303
304 tv.v_type = VAR_UNKNOWN;
305 if (eval_variable_import(extends_name, &tv) == FAIL)
306 {
307 semsg(_(e_class_name_not_found_str), extends_name);
308 return success;
309 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200310
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200311 if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
312 || (is_class
313 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
314 || (!is_class
315 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0))
316 // a interface cannot extend a class and a class cannot extend an
317 // interface.
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200318 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200319 else
320 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200321 class_T *extends_cl = tv.vval.v_class;
322 ++extends_cl->class_refcount;
323 *extends_clp = extends_cl;
324 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200325 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200326 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200327
328 return success;
329}
330
331/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200332 * Check method names in the parent class lineage to make sure the access is
333 * the same for overridden methods.
334 */
335 static int
336validate_extends_methods(
337 garray_T *objmethods_gap,
338 class_T *extends_cl)
339{
340 class_T *super = extends_cl;
341 int method_count = objmethods_gap->ga_len;
342 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
343
344 while (super != NULL)
345 {
346 int extends_method_count = super->class_obj_method_count_child;
347 if (extends_method_count == 0)
348 {
349 super = super->class_extends;
350 continue;
351 }
352
353 ufunc_T **extends_methods = super->class_obj_methods;
354
355 for (int i = 0; i < extends_method_count; i++)
356 {
357 char_u *pstr = extends_methods[i]->uf_name;
358 int extends_private = (*pstr == '_');
359 if (extends_private)
360 pstr++;
361
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200362 // When comparing the method names, ignore the access type (public
363 // and private methods are considered the same).
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200364 for (int j = 0; j < method_count; j++)
365 {
366 char_u *qstr = cl_fp[j]->uf_name;
367 int priv_method = (*qstr == '_');
368 if (priv_method)
369 qstr++;
370 if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
371 {
372 // Method access is different between the super class and
373 // the subclass
374 semsg(_(e_method_str_of_class_str_has_different_access),
375 cl_fp[j]->uf_name, super->class_name);
376 return FALSE;
377 }
378 }
379 }
380 super = super->class_extends;
381 }
382
383 return TRUE;
384}
385
386/*
387 * Check whether a object member variable in "objmembers_gap" is a duplicate of
388 * a member in any of the extended parent class lineage. Returns TRUE if there
389 * are no duplicates.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200390 */
391 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200392extends_check_dup_members(
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200393 garray_T *objmembers_gap,
394 class_T *extends_cl)
395{
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200396 int member_count = objmembers_gap->ga_len;
397 if (member_count == 0)
398 return TRUE;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200399
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200400 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
401
402 // Validate each member variable
403 for (int c_i = 0; c_i < member_count; c_i++)
404 {
405 class_T *p_cl = extends_cl;
406 ocmember_T *c_m = members + c_i;
407 char_u *pstr = (*c_m->ocm_name == '_')
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200408 ? c_m->ocm_name + 1 : c_m->ocm_name;
409
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200410 // Check in all the parent classes in the lineage
411 while (p_cl != NULL)
412 {
413 int p_member_count = p_cl->class_obj_member_count;
414 if (p_member_count == 0)
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200415 {
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200416 p_cl = p_cl->class_extends;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200417 continue;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200418 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200419 ocmember_T *p_members = p_cl->class_obj_members;
420
421 // Compare against all the members in the parent class
422 for (int p_i = 0; p_i < p_member_count; p_i++)
423 {
424 ocmember_T *p_m = p_members + p_i;
425 char_u *qstr = (*p_m->ocm_name == '_')
426 ? p_m->ocm_name + 1 : p_m->ocm_name;
427 if (STRCMP(pstr, qstr) == 0)
428 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200429 semsg(_(e_duplicate_variable_str), c_m->ocm_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200430 return FALSE;
431 }
432 }
433
434 p_cl = p_cl->class_extends;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200435 }
436 }
437
438 return TRUE;
439}
440
441/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200442 * Compare the variable type of interface variables in "objmembers_gap" against
443 * the variable in any of the extended super interface lineage. Used to
444 * compare the variable types when extending interfaces. Returns TRUE if the
445 * variable types are the same.
446 */
447 static int
448extends_check_intf_var_type(
449 garray_T *objmembers_gap,
450 class_T *extends_cl)
451{
452 int member_count = objmembers_gap->ga_len;
453 if (member_count == 0)
454 return TRUE;
455
456 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
457
458 // Validate each member variable
459 for (int c_i = 0; c_i < member_count; c_i++)
460 {
461 class_T *p_cl = extends_cl;
462 ocmember_T *c_m = members + c_i;
463 int var_found = FALSE;
464
465 // Check in all the parent classes in the lineage
466 while (p_cl != NULL && !var_found)
467 {
468 int p_member_count = p_cl->class_obj_member_count;
469 if (p_member_count == 0)
470 {
471 p_cl = p_cl->class_extends;
472 continue;
473 }
474 ocmember_T *p_members = p_cl->class_obj_members;
475
476 // Compare against all the members in the parent class
477 for (int p_i = 0; p_i < p_member_count; p_i++)
478 {
479 where_T where = WHERE_INIT;
480 ocmember_T *p_m = p_members + p_i;
481
482 if (STRCMP(p_m->ocm_name, c_m->ocm_name) != 0)
483 continue;
484
485 // Ensure the type is matching.
486 where.wt_func_name = (char *)c_m->ocm_name;
487 where.wt_kind = WT_MEMBER;
488
489 if (check_type(p_m->ocm_type, c_m->ocm_type, TRUE,
490 where) == FAIL)
491 return FALSE;
492
493 var_found = TRUE;
494 }
495
496 p_cl = p_cl->class_extends;
497 }
498 }
499
500 return TRUE;
501}
502
503/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200504 * When extending an abstract class, check whether all the abstract methods in
505 * the parent class are implemented. Returns TRUE if all the methods are
506 * implemented.
507 */
508 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200509validate_abstract_class_methods(
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200510 garray_T *classmethods_gap,
511 garray_T *objmethods_gap,
512 class_T *extends_cl)
513{
514 for (int loop = 1; loop <= 2; ++loop)
515 {
516 // loop == 1: check class methods
517 // loop == 2: check object methods
518 int extends_method_count = loop == 1
519 ? extends_cl->class_class_function_count
520 : extends_cl->class_obj_method_count;
521 if (extends_method_count == 0)
522 continue;
523
524 ufunc_T **extends_methods = loop == 1
525 ? extends_cl->class_class_functions
526 : extends_cl->class_obj_methods;
527
528 int method_count = loop == 1 ? classmethods_gap->ga_len
529 : objmethods_gap->ga_len;
530 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
531 ? classmethods_gap->ga_data
532 : objmethods_gap->ga_data);
533
534 for (int i = 0; i < extends_method_count; i++)
535 {
536 ufunc_T *uf = extends_methods[i];
Yegappan Lakshmanan1db15142023-09-19 20:34:05 +0200537 if (!IS_ABSTRACT_METHOD(uf))
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200538 continue;
539
540 int method_found = FALSE;
541
542 for (int j = 0; j < method_count; j++)
543 {
544 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
545 {
546 method_found = TRUE;
547 break;
548 }
549 }
550
551 if (!method_found)
552 {
553 semsg(_(e_abstract_method_str_not_found), uf->uf_name);
554 return FALSE;
555 }
556 }
557 }
558
559 return TRUE;
560}
561
562/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200563 * Returns TRUE if the interface variable "if_var" is present in the list of
564 * variables in "cl_mt" or in the parent lineage of one of the extended classes
565 * in "extends_cl". For a class variable, 'is_class_var' is TRUE.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200566 */
567 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200568intf_variable_present(
569 char_u *intf_class_name,
570 ocmember_T *if_var,
571 int is_class_var,
572 ocmember_T *cl_mt,
573 int cl_member_count,
574 class_T *extends_cl)
575{
576 int variable_present = FALSE;
577
578 for (int cl_i = 0; cl_i < cl_member_count; ++cl_i)
579 {
580 ocmember_T *m = &cl_mt[cl_i];
581 where_T where = WHERE_INIT;
582
583 if (STRCMP(if_var->ocm_name, m->ocm_name) != 0)
584 continue;
585
586 // Ensure the access type is same
587 if (if_var->ocm_access != m->ocm_access)
588 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200589 semsg(_(e_variable_str_of_interface_str_has_different_access),
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200590 if_var->ocm_name, intf_class_name);
591 return FALSE;
592 }
593
594 // Ensure the type is matching.
595 if (m->ocm_type == &t_any)
596 {
597 // variable type is not specified. Use the variable type in the
598 // interface.
599 m->ocm_type = if_var->ocm_type;
600 }
601 else
602 {
603 where.wt_func_name = (char *)m->ocm_name;
604 where.wt_kind = WT_MEMBER;
605 if (check_type(if_var->ocm_type, m->ocm_type, TRUE,
606 where) == FAIL)
607 return FALSE;
608 }
609
610 variable_present = TRUE;
611 break;
612 }
613
614 if (!variable_present && extends_cl != NULL)
615 {
616 int ext_cl_count = is_class_var
617 ? extends_cl->class_class_member_count
618 : extends_cl->class_obj_member_count;
619 ocmember_T *ext_cl_mt = is_class_var
620 ? extends_cl->class_class_members
621 : extends_cl->class_obj_members;
622 return intf_variable_present(intf_class_name, if_var,
623 is_class_var, ext_cl_mt,
624 ext_cl_count,
625 extends_cl->class_extends);
626 }
627
628 return variable_present;
629}
630
631/*
632 * Check the variables of the interface class "ifcl" match the class variables
633 * ("classmembers_gap") and object variables ("objmembers_gap") of a class.
634 * Returns TRUE if the class and object variables names are valid.
635 */
636 static int
637validate_interface_variables(
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200638 char_u *intf_class_name,
639 class_T *ifcl,
640 garray_T *classmembers_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200641 garray_T *objmembers_gap,
642 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200643{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200644 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200645 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200646 // loop == 1: check class variables
647 // loop == 2: check object variables
648 int is_class_var = (loop == 1);
649 int if_count = is_class_var ? ifcl->class_class_member_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200650 : ifcl->class_obj_member_count;
651 if (if_count == 0)
652 continue;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200653 ocmember_T *if_ms = is_class_var ? ifcl->class_class_members
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200654 : ifcl->class_obj_members;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200655 ocmember_T *cl_ms = (ocmember_T *)(is_class_var
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200656 ? classmembers_gap->ga_data
657 : objmembers_gap->ga_data);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200658 int cl_count = is_class_var ? classmembers_gap->ga_len
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200659 : objmembers_gap->ga_len;
660 for (int if_i = 0; if_i < if_count; ++if_i)
661 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200662 if (!intf_variable_present(intf_class_name, &if_ms[if_i],
663 is_class_var, cl_ms, cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200664 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200665 semsg(_(e_variable_str_of_interface_str_not_implemented),
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200666 if_ms[if_i].ocm_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200667 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200668 }
669 }
670 }
671
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200672 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200673}
674
675/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200676 * Returns TRUE if the method signature of "if_method" and "cl_method" matches.
677 */
678 static int
679intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method)
680{
681 where_T where = WHERE_INIT;
682
683 // Ensure the type is matching.
684 where.wt_func_name = (char *)if_method->uf_name;
685 where.wt_kind = WT_METHOD;
686 if (check_type(if_method->uf_func_type, cl_method->uf_func_type, TRUE,
687 where) == FAIL)
688 return FALSE;
689
690 return TRUE;
691}
692
693/*
694 * Returns TRUE if the interface method "if_ufunc" is present in the list of
695 * methods in "cl_fp" or in the parent lineage of one of the extended classes
696 * in "extends_cl". For a class method, 'is_class_method' is TRUE.
697 */
698 static int
699intf_method_present(
700 ufunc_T *if_ufunc,
701 int is_class_method,
702 ufunc_T **cl_fp,
703 int cl_count,
704 class_T *extends_cl)
705{
706 int method_present = FALSE;
707
708 for (int cl_i = 0; cl_i < cl_count; ++cl_i)
709 {
710 char_u *cl_name = cl_fp[cl_i]->uf_name;
711 if (STRCMP(if_ufunc->uf_name, cl_name) == 0)
712 {
713 // Ensure the type is matching.
714 if (!intf_method_type_matches(if_ufunc, cl_fp[cl_i]))
715 return FALSE;
716 method_present = TRUE;
717 break;
718 }
719 }
720
721 if (!method_present && extends_cl != NULL)
722 {
723 ufunc_T **ext_cl_fp = (ufunc_T **)(is_class_method
724 ? extends_cl->class_class_functions
725 : extends_cl->class_obj_methods);
726 int ext_cl_count = is_class_method
727 ? extends_cl->class_class_function_count
728 : extends_cl->class_obj_method_count;
729 return intf_method_present(if_ufunc, is_class_method, ext_cl_fp,
730 ext_cl_count,
731 extends_cl->class_extends);
732 }
733
734 return method_present;
735}
736
737/*
738 * Validate that a new class implements all the class/instance methods in the
739 * interface "ifcl". The new class methods are in "classfunctions_gap" and the
740 * new object methods are in "objmemthods_gap". Also validates the method
741 * types.
742 * Returns TRUE if all the interface class/object methods are implemented in
743 * the new class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200744 */
745 static int
746validate_interface_methods(
747 char_u *intf_class_name,
748 class_T *ifcl,
749 garray_T *classfunctions_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200750 garray_T *objmethods_gap,
751 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200752{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200753 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200754 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200755 // loop == 1: check class methods
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200756 // loop == 2: check object methods
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200757 int is_class_method = (loop == 1);
758 int if_count = is_class_method ? ifcl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200759 : ifcl->class_obj_method_count;
760 if (if_count == 0)
761 continue;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200762 ufunc_T **if_fp = is_class_method ? ifcl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200763 : ifcl->class_obj_methods;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200764 ufunc_T **cl_fp = (ufunc_T **)(is_class_method
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200765 ? classfunctions_gap->ga_data
766 : objmethods_gap->ga_data);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200767 int cl_count = is_class_method ? classfunctions_gap->ga_len
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200768 : objmethods_gap->ga_len;
769 for (int if_i = 0; if_i < if_count; ++if_i)
770 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200771 char_u *if_name = if_fp[if_i]->uf_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200772
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200773 if (!intf_method_present(if_fp[if_i], is_class_method, cl_fp,
774 cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200775 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200776 semsg(_(e_method_str_of_interface_str_not_implemented),
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200777 if_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200778 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200779 }
780 }
781 }
782
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200783 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200784}
785
786/*
787 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200788 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200789 * object methods and object members in the new class are in
790 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
791 * "objmembers_gap" respectively.
792 */
793 static int
794validate_implements_classes(
795 garray_T *impl_gap,
796 class_T **intf_classes,
797 garray_T *classfunctions_gap,
798 garray_T *classmembers_gap,
799 garray_T *objmethods_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200800 garray_T *objmembers_gap,
801 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200802{
803 int success = TRUE;
804
805 for (int i = 0; i < impl_gap->ga_len && success; ++i)
806 {
807 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
808 typval_T tv;
809 tv.v_type = VAR_UNKNOWN;
810 if (eval_variable_import(impl, &tv) == FAIL)
811 {
812 semsg(_(e_interface_name_not_found_str), impl);
813 success = FALSE;
814 break;
815 }
816
817 if (tv.v_type != VAR_CLASS
818 || tv.vval.v_class == NULL
819 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
820 {
821 semsg(_(e_not_valid_interface_str), impl);
822 success = FALSE;
823 clear_tv(&tv);
824 break;
825 }
826
827 class_T *ifcl = tv.vval.v_class;
828 intf_classes[i] = ifcl;
829 ++ifcl->class_refcount;
830
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200831 // check the variables of the interface match the members of the class
832 success = validate_interface_variables(impl, ifcl, classmembers_gap,
833 objmembers_gap, extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200834
835 // check the functions/methods of the interface match the
836 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200837 if (success)
838 success = validate_interface_methods(impl, ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200839 classfunctions_gap, objmethods_gap,
840 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200841 clear_tv(&tv);
842 }
843
844 return success;
845}
846
847/*
848 * Check no function argument name is used as a class member.
849 * (Object members are always accessed with "this." prefix, so no need
850 * to check them.)
851 */
852 static int
853check_func_arg_names(
854 garray_T *classfunctions_gap,
855 garray_T *objmethods_gap,
856 garray_T *classmembers_gap)
857{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200858 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200859 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200860 {
861 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
862
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200863 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200864 {
865 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
866
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200867 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200868 {
869 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
870 garray_T *mgap = classmembers_gap;
871
872 // Check all the class member names
873 for (int mi = 0; mi < mgap->ga_len; ++mi)
874 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200875 char_u *mname =
876 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200877 if (STRCMP(aname, mname) == 0)
878 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200879 if (uf->uf_script_ctx.sc_sid > 0)
880 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
881
882 semsg(_(e_argument_already_declared_in_class_str),
883 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200884
885 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200886 }
887 }
888 }
889 }
890 }
891
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200892 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200893}
894
895/*
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200896 * Returns TRUE if 'varname' is a reserved keyword name
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200897 */
898 static int
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200899is_reserved_varname(char_u *varname, char_u *varname_end)
900{
901 int reserved = FALSE;
902 char_u save_varname_end = *varname_end;
903 *varname_end = NUL;
904
905 reserved = check_reserved_name(varname, FALSE) == FAIL;
906
907 *varname_end = save_varname_end;
908
909 return reserved;
910}
911
912/*
913 * Returns TRUE if the variable "varname" is already defined either as a class
914 * variable or as an object variable.
915 */
916 static int
917is_duplicate_variable(
918 garray_T *class_members,
919 garray_T *obj_members,
920 char_u *varname,
921 char_u *varname_end)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200922{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200923 char_u *name = vim_strnsave(varname, varname_end - varname);
924 char_u *pstr = (*name == '_') ? name + 1 : name;
925 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200926
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200927 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200928 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200929 // loop == 1: class variables, loop == 2: object variables
930 garray_T *vgap = (loop == 1) ? class_members : obj_members;
931 for (int i = 0; i < vgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200932 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200933 ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i;
934 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1
935 : m->ocm_name;
936 if (STRCMP(pstr, qstr) == 0)
937 {
938 semsg(_(e_duplicate_variable_str), name);
939 dup = TRUE;
940 break;
941 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200942 }
943 }
944
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200945 vim_free(name);
946 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200947}
948
949/*
950 * Returns TRUE if the method "name" is already defined.
951 */
952 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200953is_duplicate_method(
954 garray_T *classmethods_gap,
955 garray_T *objmethods_gap,
956 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200957{
958 char_u *pstr = (*name == '_') ? name + 1 : name;
959
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200960 // loop 1: class methods, loop 2: object methods
961 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200962 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200963 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
964 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200965 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200966 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
967 char_u *qstr = *n == '_' ? n + 1 : n;
968 if (STRCMP(pstr, qstr) == 0)
969 {
970 semsg(_(e_duplicate_function_str), name);
971 return TRUE;
972 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200973 }
974 }
975
976 return FALSE;
977}
978
979/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200980 * Returns TRUE if the constructor is valid.
981 */
982 static int
983is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
984{
985 // Constructors are not allowed in abstract classes.
986 if (is_abstract)
987 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200988 emsg(_(e_cannot_define_new_method_in_abstract_class));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200989 return FALSE;
990 }
991 // A constructor is always static, no need to define it so.
992 if (has_static)
993 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200994 emsg(_(e_cannot_define_new_method_as_static));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200995 return FALSE;
996 }
997 // A return type should not be specified for the new()
998 // constructor method.
999 if (uf->uf_ret_type->tt_type != VAR_VOID)
1000 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001001 emsg(_(e_cannot_use_a_return_type_with_new_method));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001002 return FALSE;
1003 }
1004 return TRUE;
1005}
1006
1007/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001008 * Update the interface class lookup table for the member index on the
1009 * interface to the member index in the class implementing the interface.
1010 * And a lookup table for the object method index on the interface
1011 * to the object method index in the class implementing the interface.
1012 * This is also used for updating the lookup table for the extended class
1013 * hierarchy.
1014 */
1015 static int
1016update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001017 class_T *ifcl,
1018 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001019 garray_T *objmethods,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001020 int pobj_method_offset)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001021{
1022 if (ifcl == NULL)
1023 return OK;
1024
1025 // Table for members.
1026 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001027 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001028 if (if2cl == NULL)
1029 return FAIL;
1030 if2cl->i2c_next = ifcl->class_itf2class;
1031 ifcl->class_itf2class = if2cl;
1032 if2cl->i2c_class = cl;
1033 if2cl->i2c_is_method = FALSE;
1034
1035 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
1036 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
1037 {
1038 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001039 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001040 {
1041 int *table = (int *)(if2cl + 1);
1042 table[if_i] = cl_i;
1043 break;
1044 }
1045 }
1046
1047 // Table for methods.
1048 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001049 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001050 if (if2cl == NULL)
1051 return FAIL;
1052 if2cl->i2c_next = ifcl->class_itf2class;
1053 ifcl->class_itf2class = if2cl;
1054 if2cl->i2c_class = cl;
1055 if2cl->i2c_is_method = TRUE;
1056
1057 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
1058 {
1059 int done = FALSE;
1060 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
1061 {
1062 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001063 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001064 {
1065 int *table = (int *)(if2cl + 1);
1066 table[if_i] = cl_i;
1067 done = TRUE;
1068 break;
1069 }
1070 }
1071
1072 // extended class object method is not overridden by the child class.
1073 // Keep the method declared in one of the parent classes in the
1074 // lineage.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001075 if (!done)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001076 {
1077 // If "ifcl" is not the immediate parent of "cl", then search in
1078 // the intermediate parent classes.
1079 if (cl->class_extends != ifcl)
1080 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001081 class_T *parent = cl->class_extends;
1082 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001083
1084 while (!done && parent != NULL && parent != ifcl)
1085 {
1086
1087 for (int cl_i = 0;
1088 cl_i < parent->class_obj_method_count_child; ++cl_i)
1089 {
1090 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
1091 parent->class_obj_methods[cl_i]->uf_name)
1092 == 0)
1093 {
1094 int *table = (int *)(if2cl + 1);
1095 table[if_i] = method_offset + cl_i;
1096 done = TRUE;
1097 break;
1098 }
1099 }
1100 method_offset += parent->class_obj_method_count_child;
1101 parent = parent->class_extends;
1102 }
1103 }
1104
1105 if (!done)
1106 {
1107 int *table = (int *)(if2cl + 1);
1108 table[if_i] = pobj_method_offset + if_i;
1109 }
1110 }
1111 }
1112
1113 return OK;
1114}
1115
1116/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001117 * Update the member and object method lookup tables for a new class in the
1118 * interface class.
1119 * For each interface add a lookup table for the member index on the interface
1120 * to the member index in the new class. And a lookup table for the object
1121 * method index on the interface to the object method index in the new class.
1122 */
1123 static int
1124add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
1125{
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001126 // update the lookup table for all the implemented interfaces
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001127 for (int i = 0; i < cl->class_interface_count; ++i)
1128 {
1129 class_T *ifcl = cl->class_interfaces_cl[i];
1130
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001131 // update the lookup table for this interface and all its super
1132 // interfaces.
1133 while (ifcl != NULL)
1134 {
1135 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
1136 0) == FAIL)
1137 return FAIL;
1138 ifcl = ifcl->class_extends;
1139 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001140 }
1141
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001142 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001143 if (extends_cl != NULL)
1144 {
1145 class_T *pclass = extends_cl;
1146 int pobj_method_offset = objmethods_gap->ga_len;
1147
1148 // Update the entire lineage of extended classes.
1149 while (pclass != NULL)
1150 {
1151 if (update_member_method_lookup_table(pclass, cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001152 objmethods_gap, pobj_method_offset) == FAIL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001153 return FAIL;
1154
1155 pobj_method_offset += pclass->class_obj_method_count_child;
1156 pclass = pclass->class_extends;
1157 }
1158 }
1159
1160 return OK;
1161}
1162
1163/*
1164 * Add class members to a new class. Allocate a typval for each class member
1165 * and initialize it.
1166 */
1167 static void
1168add_class_members(class_T *cl, exarg_T *eap)
1169{
1170 // Allocate a typval for each class member and initialize it.
1171 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
1172 cl->class_class_member_count);
1173 if (cl->class_members_tv == NULL)
1174 return;
1175
1176 for (int i = 0; i < cl->class_class_member_count; ++i)
1177 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001178 ocmember_T *m = &cl->class_class_members[i];
1179 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001180 if (m->ocm_init != NULL)
1181 {
1182 typval_T *etv = eval_expr(m->ocm_init, eap);
1183 if (etv != NULL)
1184 {
1185 *tv = *etv;
1186 vim_free(etv);
1187 }
1188 }
1189 else
1190 {
1191 // TODO: proper default value
1192 tv->v_type = m->ocm_type->tt_type;
1193 tv->vval.v_string = NULL;
1194 }
1195 }
1196}
1197
1198/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001199 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001200 */
1201 static void
1202add_default_constructor(
1203 class_T *cl,
1204 garray_T *classfunctions_gap,
1205 garray_T *type_list_gap)
1206{
1207 garray_T fga;
1208
1209 ga_init2(&fga, 1, 1000);
1210 ga_concat(&fga, (char_u *)"new(");
1211 for (int i = 0; i < cl->class_obj_member_count; ++i)
1212 {
1213 if (i > 0)
1214 ga_concat(&fga, (char_u *)", ");
1215 ga_concat(&fga, (char_u *)"this.");
1216 ocmember_T *m = cl->class_obj_members + i;
1217 ga_concat(&fga, (char_u *)m->ocm_name);
1218 ga_concat(&fga, (char_u *)" = v:none");
1219 }
1220 ga_concat(&fga, (char_u *)")\nenddef\n");
1221 ga_append(&fga, NUL);
1222
1223 exarg_T fea;
1224 CLEAR_FIELD(fea);
1225 fea.cmdidx = CMD_def;
1226 fea.cmd = fea.arg = fga.ga_data;
1227
1228 garray_T lines_to_free;
1229 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1230
h-eastb895b0f2023-09-24 15:46:31 +02001231 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
1232 cl->class_obj_members, cl->class_obj_member_count);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001233
1234 ga_clear_strings(&lines_to_free);
1235 vim_free(fga.ga_data);
1236
1237 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1238 {
1239 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001240 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001241 ++classfunctions_gap->ga_len;
1242
1243 nf->uf_flags |= FC_NEW;
1244 nf->uf_ret_type = get_type_ptr(type_list_gap);
1245 if (nf->uf_ret_type != NULL)
1246 {
1247 nf->uf_ret_type->tt_type = VAR_OBJECT;
1248 nf->uf_ret_type->tt_class = cl;
1249 nf->uf_ret_type->tt_argcount = 0;
1250 nf->uf_ret_type->tt_args = NULL;
1251 }
1252 }
1253}
1254
1255/*
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001256 * Add the class methods and object methods to the new class "cl".
1257 * When extending a class "extends_cl", add the instance methods from the
1258 * parent class also.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001259 */
1260 static int
1261add_classfuncs_objmethods(
1262 class_T *cl,
1263 class_T *extends_cl,
1264 garray_T *classfunctions_gap,
1265 garray_T *objmethods_gap)
1266{
1267 // loop 1: class functions, loop 2: object methods
1268 for (int loop = 1; loop <= 2; ++loop)
1269 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001270 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1271 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001272 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001273 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001274 : &cl->class_obj_methods;
1275
1276 int parent_count = 0;
1277 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001278 // Include object methods from the parent.
1279 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001280 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001281 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001282 : extends_cl->class_obj_method_count;
1283
1284 *fcount = parent_count + gap->ga_len;
1285 if (*fcount == 0)
1286 {
1287 *fup = NULL;
1288 continue;
1289 }
1290 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1291 if (*fup == NULL)
1292 return FAIL;
1293
1294 if (gap->ga_len != 0)
1295 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
1296 vim_free(gap->ga_data);
1297 if (loop == 1)
1298 cl->class_class_function_count_child = gap->ga_len;
1299 else
1300 cl->class_obj_method_count_child = gap->ga_len;
1301
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001302 if (loop == 2)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001303 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001304 // Copy instance methods from the parent.
1305
1306 for (int i = 0; i < parent_count; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001307 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001308 // Can't use the same parent function, because "uf_class" is
1309 // different and compilation will have a different result.
1310 // Put them after the functions in the current class, object
1311 // methods may be overruled, then "super.Method()" is used to
1312 // find a method from the parent.
1313 ufunc_T *pf = (extends_cl->class_obj_methods)[i];
1314 (*fup)[gap->ga_len + i] = copy_function(pf);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001315
1316 // If the child class overrides a function from the parent
1317 // the signature must be equal.
1318 char_u *pname = pf->uf_name;
1319 for (int ci = 0; ci < gap->ga_len; ++ci)
1320 {
1321 ufunc_T *cf = (*fup)[ci];
1322 char_u *cname = cf->uf_name;
1323 if (STRCMP(pname, cname) == 0)
1324 {
1325 where_T where = WHERE_INIT;
1326 where.wt_func_name = (char *)pname;
1327 where.wt_kind = WT_METHOD;
1328 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1329 TRUE, where);
1330 }
1331 }
1332 }
1333 }
1334
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001335 // Set the class pointer on all the functions and object methods.
1336 for (int i = 0; i < *fcount; ++i)
1337 {
1338 ufunc_T *fp = (*fup)[i];
1339 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001340 if (i < gap->ga_len)
1341 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001342 if (loop == 2)
1343 fp->uf_flags |= FC_OBJECT;
1344 }
1345 }
1346
1347 return OK;
1348}
1349
1350/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001351 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001352 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001353 */
1354 void
1355ex_class(exarg_T *eap)
1356{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001357 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1358 long start_lnum = SOURCING_LNUM;
1359 char_u *arg = eap->arg;
1360 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001361
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001362 if (is_abstract)
1363 {
1364 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1365 {
1366 semsg(_(e_invalid_argument_str), arg);
1367 return;
1368 }
1369 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001370 is_class = TRUE;
1371 }
1372
1373 if (!current_script_is_vim9()
1374 || (cmdmod.cmod_flags & CMOD_LEGACY)
1375 || !getline_equal(eap->getline, eap->cookie, getsourceline))
1376 {
1377 if (is_class)
1378 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1379 else
1380 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1381 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001382 }
1383
1384 if (!ASCII_ISUPPER(*arg))
1385 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001386 if (is_class)
1387 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1388 else
1389 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1390 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001391 return;
1392 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001393 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1394 if (!IS_WHITE_OR_NUL(*name_end))
1395 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001396 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001397 return;
1398 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001399 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001400
Bram Moolenaara86655a2023-01-12 17:06:27 +00001401 // "export class" gets used when creating the class, don't use "is_export"
1402 // for the items inside the class.
1403 int class_export = is_export;
1404 is_export = FALSE;
1405
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001406 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001407 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001408
Bram Moolenaar83677162023-01-08 19:54:10 +00001409 // Name for "extends BaseClass"
1410 char_u *extends = NULL;
1411
Bram Moolenaar94674f22023-01-06 18:42:20 +00001412 // Names for "implements SomeInterface"
1413 garray_T ga_impl;
1414 ga_init2(&ga_impl, sizeof(char_u *), 5);
1415
1416 arg = skipwhite(name_end);
1417 while (*arg != NUL && *arg != '#' && *arg != '\n')
1418 {
1419 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001420 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001421 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1422 {
1423 if (extends != NULL)
1424 {
1425 emsg(_(e_duplicate_extends));
1426 goto early_ret;
1427 }
1428 arg = skipwhite(arg + 7);
1429 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1430 if (!IS_WHITE_OR_NUL(*end))
1431 {
1432 semsg(_(e_white_space_required_after_name_str), arg);
1433 goto early_ret;
1434 }
1435 extends = vim_strnsave(arg, end - arg);
1436 if (extends == NULL)
1437 goto early_ret;
1438
1439 arg = skipwhite(end + 1);
1440 }
1441 else if (STRNCMP(arg, "implements", 10) == 0
1442 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001443 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001444 if (!is_class)
1445 {
1446 emsg(_(e_interface_cannot_use_implements));
1447 goto early_ret;
1448 }
1449
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001450 if (ga_impl.ga_len > 0)
1451 {
1452 emsg(_(e_duplicate_implements));
1453 goto early_ret;
1454 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001455 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001456
1457 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001458 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001459 char_u *impl_end = find_name_end(arg, NULL, NULL,
1460 FNE_CHECK_START);
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001461 if ((!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1462 || (*impl_end == ','
1463 && !IS_WHITE_OR_NUL(*(impl_end + 1))))
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001464 {
1465 semsg(_(e_white_space_required_after_name_str), arg);
1466 goto early_ret;
1467 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001468 if (impl_end - arg == 0)
1469 {
1470 emsg(_(e_missing_name_after_implements));
1471 goto early_ret;
1472 }
1473
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001474 char_u *iname = vim_strnsave(arg, impl_end - arg);
1475 if (iname == NULL)
1476 goto early_ret;
1477 for (int i = 0; i < ga_impl.ga_len; ++i)
1478 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1479 {
1480 semsg(_(e_duplicate_interface_after_implements_str),
1481 iname);
1482 vim_free(iname);
1483 goto early_ret;
1484 }
1485 if (ga_add_string(&ga_impl, iname) == FAIL)
1486 {
1487 vim_free(iname);
1488 goto early_ret;
1489 }
1490 if (*impl_end != ',')
1491 {
1492 arg = skipwhite(impl_end);
1493 break;
1494 }
1495 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001496 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001497 }
1498 else
1499 {
1500 semsg(_(e_trailing_characters_str), arg);
1501early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001502 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001503 ga_clear_strings(&ga_impl);
1504 return;
1505 }
1506 }
1507
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001508 garray_T type_list; // list of pointers to allocated types
1509 ga_init2(&type_list, sizeof(type_T *), 10);
1510
Bram Moolenaard505d172022-12-18 21:42:55 +00001511 // Growarray with class members declared in the class.
1512 garray_T classmembers;
1513 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1514
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001515 // Growarray with functions declared in the class.
1516 garray_T classfunctions;
1517 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001518
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001519 // Growarray with object members declared in the class.
1520 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001521 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001522
1523 // Growarray with object methods declared in the class.
1524 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001525 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001526
1527 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001528 * Go over the body of the class/interface until "endclass" or
1529 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001530 */
1531 char_u *theline = NULL;
1532 int success = FALSE;
1533 for (;;)
1534 {
1535 vim_free(theline);
1536 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
1537 if (theline == NULL)
1538 break;
1539 char_u *line = skipwhite(theline);
1540
Bram Moolenaar418b5472022-12-20 13:38:22 +00001541 // Skip empty and comment lines.
1542 if (*line == NUL)
1543 continue;
1544 if (*line == '#')
1545 {
1546 if (vim9_bad_comment(line))
1547 break;
1548 continue;
1549 }
1550
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001551 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001552 char *end_name = is_class ? "endclass" : "endinterface";
1553 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001554 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001555 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001556 semsg(_(e_command_cannot_be_shortened_str), line);
1557 else if (*p == '|' || !ends_excmd2(line, p))
1558 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001559 else
1560 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001561 break;
1562 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001563 char *wrong_name = is_class ? "endinterface" : "endclass";
1564 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1565 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001566 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001567 break;
1568 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001569
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001570 int has_public = FALSE;
1571 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001572 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001573 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001574 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001575 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001576 break;
1577 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001578 if (!is_class)
1579 {
1580 emsg(_(e_public_member_not_supported_in_interface));
1581 break;
1582 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001583 has_public = TRUE;
1584 p = skipwhite(line + 6);
1585
Bram Moolenaard505d172022-12-18 21:42:55 +00001586 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001587 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001588 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001589 break;
1590 }
1591 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001592
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001593 int abstract_method = FALSE;
1594 char_u *pa = p;
1595 if (checkforcmd(&p, "abstract", 3))
1596 {
1597 if (STRNCMP(pa, "abstract", 8) != 0)
1598 {
1599 semsg(_(e_command_cannot_be_shortened_str), pa);
1600 break;
1601 }
1602
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001603 if (!is_class)
1604 // ignore "abstract" in an interface (as all the methods in an
1605 // interface are abstract.
1606 p = skipwhite(pa + 8);
1607 else
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001608 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001609 if (!is_abstract)
1610 {
1611 semsg(_(e_abstract_method_in_concrete_class), pa);
1612 break;
1613 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001614
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001615 abstract_method = TRUE;
1616 p = skipwhite(pa + 8);
1617 if (STRNCMP(p, "def", 3) != 0 && STRNCMP(p, "static", 6) != 0)
1618 {
1619 emsg(_(e_abstract_must_be_followed_by_def_or_static));
1620 break;
1621 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001622 }
1623 }
1624
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001625 int has_static = FALSE;
1626 char_u *ps = p;
1627 if (checkforcmd(&p, "static", 4))
1628 {
1629 if (STRNCMP(ps, "static", 6) != 0)
1630 {
1631 semsg(_(e_command_cannot_be_shortened_str), ps);
1632 break;
1633 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001634
1635 if (!is_class)
1636 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001637 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001638 break;
1639 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001640 has_static = TRUE;
1641 p = skipwhite(ps + 6);
1642 }
1643
Bram Moolenaard505d172022-12-18 21:42:55 +00001644 // object members (public, read access, private):
1645 // "this._varname"
1646 // "this.varname"
1647 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001648 if (STRNCMP(p, "this", 4) == 0)
1649 {
1650 if (p[4] != '.' || !eval_isnamec1(p[5]))
1651 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001652 semsg(_(e_invalid_object_variable_declaration_str), p);
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001653 break;
1654 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001655 if (has_static)
1656 {
1657 emsg(_(e_static_cannot_be_followed_by_this));
1658 break;
1659 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001660 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +00001661 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001662 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001663 char_u *init_expr = NULL;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001664
1665 if (!is_class && *varname == '_')
1666 {
1667 // private variables are not supported in an interface
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001668 semsg(_(e_private_variable_not_supported_in_interface),
1669 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001670 break;
1671 }
1672
Bram Moolenaard505d172022-12-18 21:42:55 +00001673 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001674 &varname_end, &type_list, &type,
1675 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001676 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001677 if (is_reserved_varname(varname, varname_end))
1678 {
1679 vim_free(init_expr);
1680 break;
1681 }
1682 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1683 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001684 {
1685 vim_free(init_expr);
1686 break;
1687 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001688 if (add_member(&objmembers, varname, varname_end,
1689 has_public, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001690 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001691 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001692 break;
1693 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001694 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001695
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001696 // constructors:
1697 // def new()
1698 // enddef
1699 // def newOther()
1700 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001701 // object methods and class functions:
1702 // def SomeMethod()
1703 // enddef
1704 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001705 // enddef
1706 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001707 // def <Tval> someMethod()
1708 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001709 else if (checkforcmd(&p, "def", 3))
1710 {
1711 exarg_T ea;
1712 garray_T lines_to_free;
1713
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001714 if (has_public)
1715 {
1716 // "public" keyword is not supported when defining an object or
1717 // class method
1718 emsg(_(e_public_keyword_not_supported_for_method));
1719 break;
1720 }
1721
1722 if (*p == NUL)
1723 {
1724 // No method name following def
1725 semsg(_(e_not_valid_command_in_class_str), line);
1726 break;
1727 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001728
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001729 CLEAR_FIELD(ea);
1730 ea.cmd = line;
1731 ea.arg = p;
1732 ea.cmdidx = CMD_def;
1733 ea.getline = eap->getline;
1734 ea.cookie = eap->cookie;
1735
1736 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001737 int class_flags;
1738 if (is_class)
1739 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
1740 else
1741 class_flags = CF_INTERFACE;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001742 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02001743 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001744 ga_clear_strings(&lines_to_free);
1745
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001746 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001747 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001748 char_u *name = uf->uf_name;
1749 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001750
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001751 if (!is_class && *name == '_')
1752 {
1753 // private variables are not supported in an interface
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001754 semsg(_(e_private_method_not_supported_in_interface),
1755 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001756 func_clear_free(uf, FALSE);
1757 break;
1758 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001759 if (is_new && !is_valid_constructor(uf, is_abstract,
1760 has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001761 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001762 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001763 break;
1764 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001765
Bram Moolenaar58b40092023-01-11 15:59:05 +00001766 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001767 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001768 {
1769 success = FALSE;
1770 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001771 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001772 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001773
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001774 garray_T *fgap = has_static || is_new
1775 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001776 if (ga_grow(fgap, 1) == OK)
1777 {
1778 if (is_new)
1779 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001780
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001781 if (abstract_method)
1782 uf->uf_flags |= FC_ABSTRACT;
1783
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001784 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1785 ++fgap->ga_len;
1786 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001787 }
1788 }
1789
1790 // class members
1791 else if (has_static)
1792 {
1793 // class members (public, read access, private):
1794 // "static _varname"
1795 // "static varname"
1796 // "public static varname"
1797 char_u *varname = p;
1798 char_u *varname_end = NULL;
1799 type_T *type = NULL;
1800 char_u *init_expr = NULL;
1801 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001802 &varname_end, &type_list, &type,
1803 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001804 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001805 if (is_reserved_varname(varname, varname_end))
1806 {
1807 vim_free(init_expr);
1808 break;
1809 }
1810 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1811 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001812 {
1813 vim_free(init_expr);
1814 break;
1815 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001816 if (add_member(&classmembers, varname, varname_end,
1817 has_public, type, init_expr) == FAIL)
1818 {
1819 vim_free(init_expr);
1820 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001821 }
1822 }
1823
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001824 else
1825 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001826 if (is_class)
1827 semsg(_(e_not_valid_command_in_class_str), line);
1828 else
1829 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001830 break;
1831 }
1832 }
1833 vim_free(theline);
1834
Bram Moolenaar83677162023-01-08 19:54:10 +00001835 class_T *extends_cl = NULL; // class from "extends" argument
1836
1837 /*
1838 * Check a few things before defining the class.
1839 */
1840
1841 // Check the "extends" class is valid.
1842 if (success && extends != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001843 success = validate_extends_class(extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00001844 VIM_CLEAR(extends);
1845
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001846 // Check the new object methods to make sure their access (public or
1847 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001848 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001849 success = validate_extends_methods(&objmethods, extends_cl);
1850
1851 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001852 // variables in the extended class lineage. If an interface is extending
1853 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001854 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001855 {
1856 if (is_class)
1857 success = extends_check_dup_members(&objmembers, extends_cl);
1858 else
1859 success = extends_check_intf_var_type(&objmembers, extends_cl);
1860 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001861
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001862 // When extending an abstract class, make sure all the abstract methods in
1863 // the parent class are implemented. If the current class is an abstract
1864 // class, then there is no need for this check.
1865 if (success && !is_abstract && extends_cl != NULL
1866 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001867 success = validate_abstract_class_methods(&classfunctions,
1868 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001869
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001870 class_T **intf_classes = NULL;
1871
Bram Moolenaar83677162023-01-08 19:54:10 +00001872 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001873 if (success && ga_impl.ga_len > 0)
1874 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001875 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1876
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001877 success = validate_implements_classes(&ga_impl, intf_classes,
1878 &classfunctions, &classmembers,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001879 &objmethods, &objmembers,
1880 extends_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001881 }
1882
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001883 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001884 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001885 success = check_func_arg_names(&classfunctions, &objmethods,
1886 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001887
Bram Moolenaareb533502022-12-14 15:06:11 +00001888 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001889 if (success)
1890 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001891 // "endclass" encountered without failures: Create the class.
1892
Bram Moolenaareb533502022-12-14 15:06:11 +00001893 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001894 if (cl == NULL)
1895 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001896 if (!is_class)
1897 cl->class_flags = CLASS_INTERFACE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001898 else if (is_abstract)
1899 cl->class_flags = CLASS_ABSTRACT;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001900
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001901 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001902 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001903 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001904 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001905
Bram Moolenaard0200c82023-01-28 15:19:40 +00001906 if (extends_cl != NULL)
1907 {
1908 cl->class_extends = extends_cl;
1909 extends_cl->class_flags |= CLASS_EXTENDED;
1910 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001911
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001912 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00001913 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001914 NULL,
1915 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00001916 &cl->class_class_members,
1917 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001918 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001919 extends_cl == NULL ? NULL
1920 : extends_cl->class_obj_members,
1921 extends_cl == NULL ? 0
1922 : extends_cl->class_obj_member_count,
1923 &cl->class_obj_members,
1924 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001925 goto cleanup;
1926
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001927 if (ga_impl.ga_len > 0)
1928 {
1929 // Move the "implements" names into the class.
1930 cl->class_interface_count = ga_impl.ga_len;
1931 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1932 if (cl->class_interfaces == NULL)
1933 goto cleanup;
1934 for (int i = 0; i < ga_impl.ga_len; ++i)
1935 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1936 VIM_CLEAR(ga_impl.ga_data);
1937 ga_impl.ga_len = 0;
1938
Bram Moolenaard0200c82023-01-28 15:19:40 +00001939 cl->class_interfaces_cl = intf_classes;
1940 intf_classes = NULL;
1941 }
1942
1943 if (cl->class_interface_count > 0 || extends_cl != NULL)
1944 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001945 // Add a method and member lookup table to each of the interface
1946 // classes.
1947 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1948 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001949 }
1950
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001951 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001952 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001953 add_class_members(cl, eap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001954
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001955 int have_new = FALSE;
1956 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001957 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001958 {
1959 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1960 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001961 {
1962 have_new = TRUE;
1963 break;
1964 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001965 }
1966
1967 if (have_new)
1968 // The return type of new() is an object of class "cl"
1969 class_func->uf_ret_type->tt_class = cl;
1970 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001971 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001972 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001973
Bram Moolenaar58b40092023-01-11 15:59:05 +00001974 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001975 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1976 &objmethods) == FAIL)
1977 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001978
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001979 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001980 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001981 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001982 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001983 cl->class_type_list = type_list;
1984
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001985 class_created(cl);
1986
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001987 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001988 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001989
1990 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001991 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02001992 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001993 typval_T tv;
1994 tv.v_type = VAR_CLASS;
1995 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001996 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001997 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001998 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001999 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002000 return;
2001 }
2002
2003cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00002004 if (cl != NULL)
2005 {
2006 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002007 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002008 if (cl->class_interfaces != NULL)
2009 {
2010 for (int i = 0; i < cl->class_interface_count; ++i)
2011 vim_free(cl->class_interfaces[i]);
2012 vim_free(cl->class_interfaces);
2013 }
2014 if (cl->class_interfaces_cl != NULL)
2015 {
2016 for (int i = 0; i < cl->class_interface_count; ++i)
2017 class_unref(cl->class_interfaces_cl[i]);
2018 vim_free(cl->class_interfaces_cl);
2019 }
Bram Moolenaareb533502022-12-14 15:06:11 +00002020 vim_free(cl->class_obj_members);
2021 vim_free(cl->class_obj_methods);
2022 vim_free(cl);
2023 }
2024
Bram Moolenaar83677162023-01-08 19:54:10 +00002025 vim_free(extends);
2026 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002027
2028 if (intf_classes != NULL)
2029 {
2030 for (int i = 0; i < ga_impl.ga_len; ++i)
2031 class_unref(intf_classes[i]);
2032 vim_free(intf_classes);
2033 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00002034 ga_clear_strings(&ga_impl);
2035
Bram Moolenaard505d172022-12-18 21:42:55 +00002036 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002037 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002038 garray_T *gap = round == 1 ? &classmembers : &objmembers;
2039 if (gap->ga_len == 0 || gap->ga_data == NULL)
2040 continue;
2041
2042 for (int i = 0; i < gap->ga_len; ++i)
2043 {
2044 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2045 vim_free(m->ocm_name);
2046 vim_free(m->ocm_init);
2047 }
2048 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002049 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002050
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002051 for (int i = 0; i < objmethods.ga_len; ++i)
2052 {
2053 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2054 func_clear_free(uf, FALSE);
2055 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002056 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002057
2058 for (int i = 0; i < classfunctions.ga_len; ++i)
2059 {
2060 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2061 func_clear_free(uf, FALSE);
2062 }
2063 ga_clear(&classfunctions);
2064
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002065 clear_type_list(&type_list);
2066}
2067
2068/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002069 * Find member "name" in class "cl", set "member_idx" to the member index and
2070 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002071 * When "is_object" is TRUE, then look for object members. Otherwise look for
2072 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002073 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002074 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002075 */
2076 type_T *
2077class_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002078 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002079 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002080 char_u *name,
2081 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002082 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002083{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002084 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002085 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002086
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002087 *member_idx = -1; // not found (yet)
2088
2089 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2090 member_idx);
2091 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002092 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002093 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2094 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002095 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002096 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002097
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002098 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002099}
2100
2101/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002102 * Handle ":enum" up to ":endenum".
2103 */
2104 void
2105ex_enum(exarg_T *eap UNUSED)
2106{
2107 // TODO
2108}
2109
2110/*
2111 * Handle ":type".
2112 */
2113 void
2114ex_type(exarg_T *eap UNUSED)
2115{
2116 // TODO
2117}
2118
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002119/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002120 * Returns OK if a member variable named "name" is present in the class "cl".
2121 * Otherwise returns FAIL. If found, the member variable typval is set in
2122 * "rettv". If "is_object" is TRUE, then the object member variable table is
2123 * searched. Otherwise the class member variable table is searched.
2124 */
2125 static int
2126get_member_tv(
2127 class_T *cl,
2128 int is_object,
2129 char_u *name,
2130 size_t namelen,
2131 typval_T *rettv)
2132{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002133 ocmember_T *m;
2134 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002135
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002136 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2137 &m_idx);
2138 if (m == NULL)
2139 return FAIL;
2140
2141 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002142 {
RestorerZ7fe8f432023-09-24 23:21:24 +02002143 semsg(_(e_cannot_access_private_variable_str), m->ocm_name);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002144 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002145 }
2146
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002147 // The object only contains a pointer to the class, the member
2148 // values array follows right after that.
2149 object_T *obj = rettv->vval.v_object;
2150 if (is_object)
2151 {
2152 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2153 copy_tv(tv, rettv);
2154 }
2155 else
2156 copy_tv(&cl->class_members_tv[m_idx], rettv);
2157
2158 object_unref(obj);
2159
2160 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002161}
2162
2163/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002164 * Evaluate what comes after a class:
2165 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002166 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002167 * - class constructor: SomeClass.new()
2168 * - object member: someObject.varname
2169 * - object method: someObject.SomeMethod()
2170 *
2171 * "*arg" points to the '.'.
2172 * "*arg" is advanced to after the member name or method call.
2173 *
2174 * Returns FAIL or OK.
2175 */
2176 int
2177class_object_index(
2178 char_u **arg,
2179 typval_T *rettv,
2180 evalarg_T *evalarg,
2181 int verbose UNUSED) // give error messages
2182{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002183 if (VIM_ISWHITE((*arg)[1]))
2184 {
2185 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
2186 return FAIL;
2187 }
2188
2189 ++*arg;
2190 char_u *name = *arg;
2191 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2192 if (name_end == name)
2193 return FAIL;
2194 size_t len = name_end - name;
2195
Bram Moolenaar552bdca2023-02-17 21:08:50 +00002196 class_T *cl;
2197 if (rettv->v_type == VAR_CLASS)
2198 cl = rettv->vval.v_class;
2199 else // VAR_OBJECT
2200 {
2201 if (rettv->vval.v_object == NULL)
2202 {
2203 emsg(_(e_using_null_object));
2204 return FAIL;
2205 }
2206 cl = rettv->vval.v_object->obj_class;
2207 }
2208
Bram Moolenaard13dd302023-03-11 20:56:35 +00002209 if (cl == NULL)
2210 {
2211 emsg(_(e_incomplete_type));
2212 return FAIL;
2213 }
2214
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002215 if (*name_end == '(')
2216 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002217 ufunc_T *fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002218
Ernie Rael4d00b832023-09-11 19:54:42 +02002219 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002220 if (fp == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002221 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002222 method_not_found_msg(cl, rettv->v_type, name, len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002223 return FAIL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002224 }
2225
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002226 typval_T argvars[MAX_FUNC_ARGS + 1];
2227 int argcount = 0;
2228
2229 if (*fp->uf_name == '_')
2230 {
2231 // Cannot access a private method outside of a class
2232 semsg(_(e_cannot_access_private_method_str), name);
2233 return FAIL;
2234 }
2235
2236 char_u *argp = name_end;
2237 int ret = get_func_arguments(&argp, evalarg, 0,
2238 argvars, &argcount);
2239 if (ret == FAIL)
2240 return FAIL;
2241
2242 funcexe_T funcexe;
2243 CLEAR_FIELD(funcexe);
2244 funcexe.fe_evaluate = TRUE;
2245 if (rettv->v_type == VAR_OBJECT)
2246 {
2247 funcexe.fe_object = rettv->vval.v_object;
2248 ++funcexe.fe_object->obj_refcount;
2249 }
2250
2251 // Clear the class or object after calling the function, in
2252 // case the refcount is one.
2253 typval_T tv_tofree = *rettv;
2254 rettv->v_type = VAR_UNKNOWN;
2255
2256 // Call the user function. Result goes into rettv;
2257 int error = call_user_func_check(fp, argcount, argvars,
2258 rettv, &funcexe, NULL);
2259
2260 // Clear the previous rettv and the arguments.
2261 clear_tv(&tv_tofree);
2262 for (int idx = 0; idx < argcount; ++idx)
2263 clear_tv(&argvars[idx]);
2264
2265 if (error != FCERR_NONE)
2266 {
2267 user_func_error(error, printable_func_name(fp),
2268 funcexe.fe_found_var);
2269 return FAIL;
2270 }
2271 *arg = argp;
2272 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002273 }
2274
2275 else if (rettv->v_type == VAR_OBJECT)
2276 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002277 // Search in the object member variable table and the class member
2278 // variable table.
Yegappan Lakshmanan23c92d92023-09-09 11:33:29 +02002279 if (get_member_tv(cl, TRUE, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002280 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002281 *arg = name_end;
2282 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002283 }
2284
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002285 member_not_found_msg(cl, VAR_OBJECT, name, len);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002286 }
2287
Bram Moolenaard505d172022-12-18 21:42:55 +00002288 else if (rettv->v_type == VAR_CLASS)
2289 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002290 int m_idx;
2291
Bram Moolenaard505d172022-12-18 21:42:55 +00002292 // class member
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002293 ocmember_T *m = class_member_lookup(cl, name, len, &m_idx);
2294 if (m == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002295 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002296 member_not_found_msg(cl, VAR_CLASS, name, len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002297 return FAIL;
Bram Moolenaard505d172022-12-18 21:42:55 +00002298 }
2299
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002300 if (*name == '_')
2301 {
RestorerZ7fe8f432023-09-24 23:21:24 +02002302 semsg(_(e_cannot_access_private_variable_str), m->ocm_name);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002303 return FAIL;
2304 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002305
2306 typval_T *tv = &cl->class_members_tv[m_idx];
2307 copy_tv(tv, rettv);
2308 class_unref(cl);
2309
2310 *arg = name_end;
2311 return OK;
Bram Moolenaard505d172022-12-18 21:42:55 +00002312 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002313
2314 return FAIL;
2315}
2316
2317/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002318 * If "arg" points to a class or object method, return it.
2319 * Otherwise return NULL.
2320 */
2321 ufunc_T *
2322find_class_func(char_u **arg)
2323{
2324 char_u *name = *arg;
2325 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2326 if (name_end == name || *name_end != '.')
2327 return NULL;
2328
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002329 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002330 size_t len = name_end - name;
2331 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002332 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00002333 if (eval_variable(name, (int)len,
2334 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002335 return NULL;
2336 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00002337 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002338
2339 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
2340 : tv.vval.v_object->obj_class;
2341 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002342 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002343 char_u *fname = name_end + 1;
2344 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
2345 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00002346 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002347 len = fname_end - fname;
2348
Ernie Rael4d00b832023-09-11 19:54:42 +02002349 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002350
Bram Moolenaareb533502022-12-14 15:06:11 +00002351fail_after_eval:
2352 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002353 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002354}
2355
2356/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002357 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002358 * Returns -1, if the variable is not found.
2359 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002360 */
2361 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002362class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002363{
Ernie Rael4d00b832023-09-11 19:54:42 +02002364 int idx;
2365 class_member_lookup(cl, name, namelen, &idx);
2366 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002367}
2368
2369/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002370 * Returns a pointer to the class member variable "name" in the class "cl".
2371 * Returns NULL if the variable is not found.
2372 * The member variable index is set in "idx".
2373 */
2374 ocmember_T *
2375class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2376{
Ernie Rael4d00b832023-09-11 19:54:42 +02002377 ocmember_T *ret_m = NULL;
2378 int ret_idx = -1;
2379 for (int i = 0; i < cl->class_class_member_count; ++i)
2380 {
2381 ocmember_T *m = &cl->class_class_members[i];
2382 if (namelen)
2383 {
2384 if (STRNCMP(name, m->ocm_name, namelen) == 0
2385 && m->ocm_name[namelen] == NUL)
2386 {
2387 ret_m = m;
2388 ret_idx = i;
2389 break;
2390 }
2391 }
2392 else if (STRCMP(name, m->ocm_name) == 0)
2393 {
2394 ret_m = m;
2395 ret_idx = i;
2396 break;
2397 }
2398 }
2399 if (idx != NULL)
2400 *idx = ret_idx;
2401 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002402}
2403
2404/*
2405 * Returns the index of class method "name" in the class "cl".
2406 * Returns -1, if the method is not found.
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002407 */
2408 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002409class_method_idx(class_T *cl, char_u *name, size_t namelen)
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002410{
Ernie Rael4d00b832023-09-11 19:54:42 +02002411 int idx;
2412 class_method_lookup(cl, name, namelen, &idx);
2413 return idx;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002414}
2415
2416/*
2417 * Returns a pointer to the class method "name" in class "cl".
2418 * Returns NULL if the method is not found.
2419 * The method index is set in "idx".
2420 */
2421 ufunc_T *
2422class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2423{
Ernie Rael4d00b832023-09-11 19:54:42 +02002424 ufunc_T *ret_fp = NULL;
2425 int ret_idx = -1;
2426 for (int i = 0; i < cl->class_class_function_count; ++i)
2427 {
2428 ufunc_T *fp = cl->class_class_functions[i];
2429 char_u *ufname = (char_u *)fp->uf_name;
2430 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2431 {
2432 ret_fp = fp;
2433 ret_idx = i;
2434 break;
2435 }
2436 }
2437 if (idx != NULL)
2438 *idx = ret_idx;
2439 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002440}
2441
2442/*
2443 * Returns the index of object member variable "name" in the class "cl".
2444 * Returns -1, if the variable is not found.
2445 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
2446 */
2447 int
2448object_member_idx(class_T *cl, char_u *name, size_t namelen)
2449{
Ernie Rael4d00b832023-09-11 19:54:42 +02002450 int idx;
2451 object_member_lookup(cl, name, namelen, &idx);
2452 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002453}
2454
2455/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002456 * Returns a pointer to the object member variable "name" in the class "cl".
2457 * Returns NULL if the variable is not found.
2458 * The object member variable index is set in "idx".
2459 */
2460 ocmember_T *
2461object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2462{
Ernie Rael4d00b832023-09-11 19:54:42 +02002463 ocmember_T *ret_m = NULL;
2464 int ret_idx = -1;
2465 for (int i = 0; i < cl->class_obj_member_count; ++i)
2466 {
2467 ocmember_T *m = &cl->class_obj_members[i];
2468 if (namelen)
2469 {
2470 if (STRNCMP(name, m->ocm_name, namelen) == 0
2471 && m->ocm_name[namelen] == NUL)
2472 {
2473 ret_m = m;
2474 ret_idx = i;
2475 break;
2476 }
2477 }
2478 else if (STRCMP(name, m->ocm_name) == 0)
2479 {
2480 ret_m = m;
2481 ret_idx = i;
2482 break;
2483 }
2484 }
2485 if (idx != NULL)
2486 *idx = ret_idx;
2487 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002488}
2489
2490/*
2491 * Returns the index of object method "name" in the class "cl".
2492 * Returns -1, if the method is not found.
2493 */
2494 int
2495object_method_idx(class_T *cl, char_u *name, size_t namelen)
2496{
Ernie Rael4d00b832023-09-11 19:54:42 +02002497 int idx;
2498 object_method_lookup(cl, name, namelen, &idx);
2499 return idx;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002500}
2501
2502/*
2503 * Returns a pointer to the object method "name" in class "cl".
2504 * Returns NULL if the method is not found.
2505 * The object method index is set in "idx".
2506 */
2507 ufunc_T *
2508object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2509{
Ernie Rael4d00b832023-09-11 19:54:42 +02002510 ufunc_T *ret_fp = NULL;
2511 int ret_idx = -1;
2512 for (int i = 0; i < cl->class_obj_method_count; ++i)
2513 {
2514 ufunc_T *fp = cl->class_obj_methods[i];
2515 // Use a separate pointer to avoid that ASAN complains about
2516 // uf_name[] only being 4 characters.
2517 char_u *ufname = (char_u *)fp->uf_name;
2518 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2519 {
2520 ret_fp = fp;
2521 ret_idx = i;
2522 break;
2523 }
2524 }
2525 if (idx != NULL)
2526 *idx = ret_idx;
2527 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002528}
2529
2530/*
2531 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
2532 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
2533 * object member variable.
2534 *
2535 * Returns a pointer to the member variable structure if variable is found.
2536 * Otherwise returns NULL. The member variable index is set in "*idx".
2537 */
2538 ocmember_T *
2539member_lookup(
2540 class_T *cl,
2541 vartype_T v_type,
2542 char_u *name,
2543 size_t namelen,
2544 int *idx)
2545{
2546 if (v_type == VAR_CLASS)
2547 return class_member_lookup(cl, name, namelen, idx);
2548 else
2549 return object_member_lookup(cl, name, namelen, idx);
2550}
2551
2552/*
2553 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
2554 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
2555 *
2556 * Returns a pointer to the method structure if variable is found.
2557 * Otherwise returns NULL. The method variable index is set in "*idx".
2558 */
2559 ufunc_T *
2560method_lookup(
2561 class_T *cl,
2562 vartype_T v_type,
2563 char_u *name,
2564 size_t namelen,
2565 int *idx)
2566{
2567 if (v_type == VAR_CLASS)
2568 return class_method_lookup(cl, name, namelen, idx);
2569 else
2570 return object_method_lookup(cl, name, namelen, idx);
2571}
2572
2573/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00002574 * Return TRUE if current context "cctx_arg" is inside class "cl".
2575 * Return FALSE if not.
2576 */
2577 int
2578inside_class(cctx_T *cctx_arg, class_T *cl)
2579{
2580 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002581 if (cctx->ctx_ufunc != NULL
2582 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002583 return TRUE;
2584 return FALSE;
2585}
2586
2587/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002588 * Make a copy of an object.
2589 */
2590 void
2591copy_object(typval_T *from, typval_T *to)
2592{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002593 if (from->vval.v_object == NULL)
2594 to->vval.v_object = NULL;
2595 else
2596 {
2597 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002598 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002599 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002600}
2601
2602/*
2603 * Free an object.
2604 */
2605 static void
2606object_clear(object_T *obj)
2607{
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002608 // Avoid a recursive call, it can happen if "obj" has a circular reference.
2609 obj->obj_refcount = INT_MAX;
2610
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002611 class_T *cl = obj->obj_class;
2612
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002613 if (!cl)
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002614 return;
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002615
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002616 // the member values are just after the object structure
2617 typval_T *tv = (typval_T *)(obj + 1);
2618 for (int i = 0; i < cl->class_obj_member_count; ++i)
2619 clear_tv(tv + i);
2620
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002621 // Remove from the list headed by "first_object".
2622 object_cleared(obj);
2623
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002624 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002625 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002626}
2627
2628/*
2629 * Unreference an object.
2630 */
2631 void
2632object_unref(object_T *obj)
2633{
2634 if (obj != NULL && --obj->obj_refcount <= 0)
2635 object_clear(obj);
2636}
2637
2638/*
2639 * Make a copy of a class.
2640 */
2641 void
2642copy_class(typval_T *from, typval_T *to)
2643{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002644 if (from->vval.v_class == NULL)
2645 to->vval.v_class = NULL;
2646 else
2647 {
2648 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002649 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002650 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002651}
2652
2653/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002654 * Free the class "cl" and its contents.
2655 */
2656 static void
2657class_free(class_T *cl)
2658{
2659 // Freeing what the class contains may recursively come back here.
2660 // Clear "class_name" first, if it is NULL the class does not need to
2661 // be freed.
2662 VIM_CLEAR(cl->class_name);
2663
2664 class_unref(cl->class_extends);
2665
2666 for (int i = 0; i < cl->class_interface_count; ++i)
2667 {
2668 vim_free(((char_u **)cl->class_interfaces)[i]);
2669 if (cl->class_interfaces_cl[i] != NULL)
2670 class_unref(cl->class_interfaces_cl[i]);
2671 }
2672 vim_free(cl->class_interfaces);
2673 vim_free(cl->class_interfaces_cl);
2674
2675 itf2class_T *next;
2676 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2677 {
2678 next = i2c->i2c_next;
2679 vim_free(i2c);
2680 }
2681
2682 for (int i = 0; i < cl->class_class_member_count; ++i)
2683 {
2684 ocmember_T *m = &cl->class_class_members[i];
2685 vim_free(m->ocm_name);
2686 vim_free(m->ocm_init);
2687 if (cl->class_members_tv != NULL)
2688 clear_tv(&cl->class_members_tv[i]);
2689 }
2690 vim_free(cl->class_class_members);
2691 vim_free(cl->class_members_tv);
2692
2693 for (int i = 0; i < cl->class_obj_member_count; ++i)
2694 {
2695 ocmember_T *m = &cl->class_obj_members[i];
2696 vim_free(m->ocm_name);
2697 vim_free(m->ocm_init);
2698 }
2699 vim_free(cl->class_obj_members);
2700
2701 for (int i = 0; i < cl->class_class_function_count; ++i)
2702 {
2703 ufunc_T *uf = cl->class_class_functions[i];
2704 func_clear_free(uf, FALSE);
2705 }
2706 vim_free(cl->class_class_functions);
2707
2708 for (int i = 0; i < cl->class_obj_method_count; ++i)
2709 {
2710 ufunc_T *uf = cl->class_obj_methods[i];
2711 func_clear_free(uf, FALSE);
2712 }
2713 vim_free(cl->class_obj_methods);
2714
2715 clear_type_list(&cl->class_type_list);
2716
2717 class_cleared(cl);
2718
2719 vim_free(cl);
2720}
2721
2722/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002723 * Unreference a class. Free it when the reference count goes down to zero.
2724 */
2725 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002726class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002727{
Bram Moolenaard505d172022-12-18 21:42:55 +00002728 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002729 class_free(cl);
2730}
2731
2732/*
2733 * Go through the list of all classes and free items without "copyID".
2734 */
2735 int
2736class_free_nonref(int copyID)
2737{
2738 int did_free = FALSE;
2739
2740 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002741 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002742 next_nonref_class = cl->class_next_used;
2743 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002744 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002745 // Free the class and items it contains.
2746 class_free(cl);
2747 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002748 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002749 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002750
2751 next_nonref_class = NULL;
2752 return did_free;
2753}
2754
2755 int
2756set_ref_in_classes(int copyID)
2757{
2758 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
2759 set_ref_in_item_class(cl, copyID, NULL, NULL);
2760
2761 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002762}
2763
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002764static object_T *first_object = NULL;
2765
2766/*
2767 * Call this function when an object has been created. It will be added to the
2768 * list headed by "first_object".
2769 */
2770 void
2771object_created(object_T *obj)
2772{
2773 if (first_object != NULL)
2774 {
2775 obj->obj_next_used = first_object;
2776 first_object->obj_prev_used = obj;
2777 }
2778 first_object = obj;
2779}
2780
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002781static object_T *next_nonref_obj = NULL;
2782
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002783/*
2784 * Call this function when an object has been cleared and is about to be freed.
2785 * It is removed from the list headed by "first_object".
2786 */
2787 void
2788object_cleared(object_T *obj)
2789{
2790 if (obj->obj_next_used != NULL)
2791 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
2792 if (obj->obj_prev_used != NULL)
2793 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
2794 else if (first_object == obj)
2795 first_object = obj->obj_next_used;
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002796
2797 // update the next object to check if needed
2798 if (obj == next_nonref_obj)
2799 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002800}
2801
2802/*
2803 * Go through the list of all objects and free items without "copyID".
2804 */
2805 int
2806object_free_nonref(int copyID)
2807{
2808 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002809
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002810 for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002811 {
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002812 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002813 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
2814 {
2815 // Free the object and items it contains.
2816 object_clear(obj);
2817 did_free = TRUE;
2818 }
2819 }
2820
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002821 next_nonref_obj = NULL;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002822 return did_free;
2823}
2824
LemonBoyafe04662023-08-23 21:08:11 +02002825/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002826 * Echo a class or object method not found message.
2827 */
2828 void
2829method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
2830{
2831 char_u *method_name = vim_strnsave(name, len);
2832 if ((v_type == VAR_OBJECT)
2833 && (class_method_idx(cl, name, len) >= 0))
2834 {
2835 // If this is a class method, then give a different error
2836 if (*name == '_')
2837 semsg(_(e_cannot_access_private_method_str), method_name);
2838 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002839 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002840 method_name, cl->class_name);
2841 }
2842 else if ((v_type == VAR_CLASS)
2843 && (object_method_idx(cl, name, len) >= 0))
2844 {
2845 // If this is an object method, then give a different error
2846 if (*name == '_')
2847 semsg(_(e_cannot_access_private_method_str), method_name);
2848 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002849 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002850 method_name, cl->class_name);
2851 }
2852 else
2853 semsg(_(e_method_not_found_on_class_str_str), cl->class_name,
2854 method_name);
2855 vim_free(method_name);
2856}
2857
2858/*
2859 * Echo a class or object member not found message.
2860 */
2861 void
2862member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
2863{
2864 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
2865
2866 if (v_type == VAR_OBJECT)
2867 {
2868 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02002869 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002870 varname, cl->class_name);
2871 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002872 semsg(_(e_variable_not_found_on_object_str_str), cl->class_name,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002873 varname);
2874 }
2875 else
2876 {
2877 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02002878 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002879 varname, cl->class_name);
2880 else
RestorerZ7fe8f432023-09-24 23:21:24 +02002881 semsg(_(e_class_variable_str_not_found_in_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002882 varname, cl->class_name);
2883 }
2884 vim_free(varname);
2885}
2886
2887/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002888 * Return TRUE when the class "cl", its base class or one of the implemented
2889 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02002890 */
2891 int
2892class_instance_of(class_T *cl, class_T *other_cl)
2893{
2894 if (cl == other_cl)
2895 return TRUE;
2896
2897 // Recursively check the base classes.
2898 for (; cl != NULL; cl = cl->class_extends)
2899 {
2900 if (cl == other_cl)
2901 return TRUE;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002902 // Check the implemented interfaces and the super interfaces
LemonBoyafe04662023-08-23 21:08:11 +02002903 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002904 {
2905 class_T *intf = cl->class_interfaces_cl[i];
2906 while (intf != NULL)
2907 {
2908 if (intf == other_cl)
2909 return TRUE;
2910 // check the super interfaces
2911 intf = intf->class_extends;
2912 }
2913 }
LemonBoyafe04662023-08-23 21:08:11 +02002914 }
2915
2916 return FALSE;
2917}
2918
2919/*
2920 * "instanceof(object, classinfo)" function
2921 */
2922 void
2923f_instanceof(typval_T *argvars, typval_T *rettv)
2924{
2925 typval_T *object_tv = &argvars[0];
2926 typval_T *classinfo_tv = &argvars[1];
2927 listitem_T *li;
2928
2929 rettv->vval.v_number = VVAL_FALSE;
2930
2931 if (check_for_object_arg(argvars, 0) == FAIL
2932 || check_for_class_or_list_arg(argvars, 1) == FAIL)
2933 return;
2934
Ernie Rael3da696d2023-09-19 20:14:18 +02002935 if (object_tv->vval.v_object == NULL)
2936 return;
2937
LemonBoyafe04662023-08-23 21:08:11 +02002938 if (classinfo_tv->v_type == VAR_LIST)
2939 {
2940 FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
2941 {
2942 if (li->li_tv.v_type != VAR_CLASS)
2943 {
2944 emsg(_(e_class_required));
2945 return;
2946 }
2947
2948 if (class_instance_of(object_tv->vval.v_object->obj_class,
2949 li->li_tv.vval.v_class) == TRUE)
2950 {
2951 rettv->vval.v_number = VVAL_TRUE;
2952 return;
2953 }
2954 }
2955 }
2956 else if (classinfo_tv->v_type == VAR_CLASS)
2957 {
2958 rettv->vval.v_number = class_instance_of(object_tv->vval.v_object->obj_class,
2959 classinfo_tv->vval.v_class);
2960 }
2961}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002962
2963#endif // FEAT_EVAL