blob: a36c482d16c2c44edc59dfae2fe0c2296297ac4c [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 {
82 semsg(_(e_public_member_name_cannot_start_with_underscore_str), line);
83 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 {
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200116 emsg(_(e_cannot_initialize_member_in_interface));
117 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
296validate_extends_class(char_u *extends_name, class_T **extends_clp)
297{
298 typval_T tv;
299 int success = FALSE;
300
301 tv.v_type = VAR_UNKNOWN;
302 if (eval_variable_import(extends_name, &tv) == FAIL)
303 {
304 semsg(_(e_class_name_not_found_str), extends_name);
305 return success;
306 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200307
308 if (tv.v_type != VAR_CLASS
309 || tv.vval.v_class == NULL
310 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
311 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200312 else
313 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200314 class_T *extends_cl = tv.vval.v_class;
315 ++extends_cl->class_refcount;
316 *extends_clp = extends_cl;
317 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200318 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200319 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200320
321 return success;
322}
323
324/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200325 * Check method names in the parent class lineage to make sure the access is
326 * the same for overridden methods.
327 */
328 static int
329validate_extends_methods(
330 garray_T *objmethods_gap,
331 class_T *extends_cl)
332{
333 class_T *super = extends_cl;
334 int method_count = objmethods_gap->ga_len;
335 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
336
337 while (super != NULL)
338 {
339 int extends_method_count = super->class_obj_method_count_child;
340 if (extends_method_count == 0)
341 {
342 super = super->class_extends;
343 continue;
344 }
345
346 ufunc_T **extends_methods = super->class_obj_methods;
347
348 for (int i = 0; i < extends_method_count; i++)
349 {
350 char_u *pstr = extends_methods[i]->uf_name;
351 int extends_private = (*pstr == '_');
352 if (extends_private)
353 pstr++;
354
355 for (int j = 0; j < method_count; j++)
356 {
357 char_u *qstr = cl_fp[j]->uf_name;
358 int priv_method = (*qstr == '_');
359 if (priv_method)
360 qstr++;
361 if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
362 {
363 // Method access is different between the super class and
364 // the subclass
365 semsg(_(e_method_str_of_class_str_has_different_access),
366 cl_fp[j]->uf_name, super->class_name);
367 return FALSE;
368 }
369 }
370 }
371 super = super->class_extends;
372 }
373
374 return TRUE;
375}
376
377/*
378 * Check whether a object member variable in "objmembers_gap" is a duplicate of
379 * a member in any of the extended parent class lineage. Returns TRUE if there
380 * are no duplicates.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200381 */
382 static int
383validate_extends_members(
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200384 garray_T *objmembers_gap,
385 class_T *extends_cl)
386{
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200387 // loop == 1: check class members
388 // loop == 2: check object members
389 int member_count = objmembers_gap->ga_len;
390 if (member_count == 0)
391 return TRUE;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200392
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200393 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
394
395 // Validate each member variable
396 for (int c_i = 0; c_i < member_count; c_i++)
397 {
398 class_T *p_cl = extends_cl;
399 ocmember_T *c_m = members + c_i;
400 char_u *pstr = (*c_m->ocm_name == '_')
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200401 ? c_m->ocm_name + 1 : c_m->ocm_name;
402
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200403 // Check in all the parent classes in the lineage
404 while (p_cl != NULL)
405 {
406 int p_member_count = p_cl->class_obj_member_count;
407 if (p_member_count == 0)
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200408 {
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200409 p_cl = p_cl->class_extends;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200410 continue;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200411 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200412 ocmember_T *p_members = p_cl->class_obj_members;
413
414 // Compare against all the members in the parent class
415 for (int p_i = 0; p_i < p_member_count; p_i++)
416 {
417 ocmember_T *p_m = p_members + p_i;
418 char_u *qstr = (*p_m->ocm_name == '_')
419 ? p_m->ocm_name + 1 : p_m->ocm_name;
420 if (STRCMP(pstr, qstr) == 0)
421 {
422 semsg(_(e_duplicate_member_str), c_m->ocm_name);
423 return FALSE;
424 }
425 }
426
427 p_cl = p_cl->class_extends;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200428 }
429 }
430
431 return TRUE;
432}
433
434/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200435 * When extending an abstract class, check whether all the abstract methods in
436 * the parent class are implemented. Returns TRUE if all the methods are
437 * implemented.
438 */
439 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200440validate_abstract_class_methods(
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200441 garray_T *classmethods_gap,
442 garray_T *objmethods_gap,
443 class_T *extends_cl)
444{
445 for (int loop = 1; loop <= 2; ++loop)
446 {
447 // loop == 1: check class methods
448 // loop == 2: check object methods
449 int extends_method_count = loop == 1
450 ? extends_cl->class_class_function_count
451 : extends_cl->class_obj_method_count;
452 if (extends_method_count == 0)
453 continue;
454
455 ufunc_T **extends_methods = loop == 1
456 ? extends_cl->class_class_functions
457 : extends_cl->class_obj_methods;
458
459 int method_count = loop == 1 ? classmethods_gap->ga_len
460 : objmethods_gap->ga_len;
461 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
462 ? classmethods_gap->ga_data
463 : objmethods_gap->ga_data);
464
465 for (int i = 0; i < extends_method_count; i++)
466 {
467 ufunc_T *uf = extends_methods[i];
468 if ((uf->uf_flags & FC_ABSTRACT) == 0)
469 continue;
470
471 int method_found = FALSE;
472
473 for (int j = 0; j < method_count; j++)
474 {
475 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
476 {
477 method_found = TRUE;
478 break;
479 }
480 }
481
482 if (!method_found)
483 {
484 semsg(_(e_abstract_method_str_not_found), uf->uf_name);
485 return FALSE;
486 }
487 }
488 }
489
490 return TRUE;
491}
492
493/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200494 * Check the members of the interface class "ifcl" match the class members
495 * ("classmembers_gap") and object members ("objmembers_gap") of a class.
496 * Returns TRUE if the class and object member names are valid.
497 */
498 static int
499validate_interface_members(
500 char_u *intf_class_name,
501 class_T *ifcl,
502 garray_T *classmembers_gap,
503 garray_T *objmembers_gap)
504{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200505 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200506 {
507 // loop == 1: check class members
508 // loop == 2: check object members
509 int if_count = loop == 1 ? ifcl->class_class_member_count
510 : ifcl->class_obj_member_count;
511 if (if_count == 0)
512 continue;
513 ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
514 : ifcl->class_obj_members;
515 ocmember_T *cl_ms = (ocmember_T *)(loop == 1
516 ? classmembers_gap->ga_data
517 : objmembers_gap->ga_data);
518 int cl_count = loop == 1 ? classmembers_gap->ga_len
519 : objmembers_gap->ga_len;
520 for (int if_i = 0; if_i < if_count; ++if_i)
521 {
522 int cl_i;
523 for (cl_i = 0; cl_i < cl_count; ++cl_i)
524 {
525 ocmember_T *m = &cl_ms[cl_i];
526 where_T where = WHERE_INIT;
527
528 if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) != 0)
529 continue;
530
531 // Ensure the type is matching.
532 where.wt_func_name = (char *)m->ocm_name;
533 where.wt_kind = WT_MEMBER;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200534 if (check_type(if_ms[if_i].ocm_type, m->ocm_type, TRUE,
535 where) == FAIL)
536 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200537
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200538 if (if_ms[if_i].ocm_access != m->ocm_access)
539 {
540 semsg(_(e_member_str_of_interface_str_has_different_access),
541 if_ms[if_i].ocm_name, intf_class_name);
542 return FALSE;
543 }
544
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200545 break;
546 }
547 if (cl_i == cl_count)
548 {
549 semsg(_(e_member_str_of_interface_str_not_implemented),
550 if_ms[if_i].ocm_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200551 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200552 }
553 }
554 }
555
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200556 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200557}
558
559/*
560 * Check the functions/methods of the interface class "ifcl" match the class
561 * methods ("classfunctions_gap") and object functions ("objmemthods_gap") of a
562 * class.
563 * Returns TRUE if the class and object member names are valid.
564 */
565 static int
566validate_interface_methods(
567 char_u *intf_class_name,
568 class_T *ifcl,
569 garray_T *classfunctions_gap,
570 garray_T *objmethods_gap)
571{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200572 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200573 {
574 // loop == 1: check class functions
575 // loop == 2: check object methods
576 int if_count = loop == 1 ? ifcl->class_class_function_count
577 : ifcl->class_obj_method_count;
578 if (if_count == 0)
579 continue;
580 ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
581 : ifcl->class_obj_methods;
582 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
583 ? classfunctions_gap->ga_data
584 : objmethods_gap->ga_data);
585 int cl_count = loop == 1 ? classfunctions_gap->ga_len
586 : objmethods_gap->ga_len;
587 for (int if_i = 0; if_i < if_count; ++if_i)
588 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200589 char_u *if_name = if_fp[if_i]->uf_name;
590 int cl_i;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200591 for (cl_i = 0; cl_i < cl_count; ++cl_i)
592 {
593 char_u *cl_name = cl_fp[cl_i]->uf_name;
594 if (STRCMP(if_name, cl_name) == 0)
595 {
596 where_T where = WHERE_INIT;
597
598 // Ensure the type is matching.
599 where.wt_func_name = (char *)if_name;
600 where.wt_kind = WT_METHOD;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200601 if (check_type(if_fp[if_i]->uf_func_type,
602 cl_fp[cl_i]->uf_func_type, TRUE, where) == FAIL)
603 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200604 break;
605 }
606 }
607 if (cl_i == cl_count)
608 {
609 semsg(_(e_function_str_of_interface_str_not_implemented),
610 if_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200611 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200612 }
613 }
614 }
615
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200616 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200617}
618
619/*
620 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200621 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200622 * object methods and object members in the new class are in
623 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
624 * "objmembers_gap" respectively.
625 */
626 static int
627validate_implements_classes(
628 garray_T *impl_gap,
629 class_T **intf_classes,
630 garray_T *classfunctions_gap,
631 garray_T *classmembers_gap,
632 garray_T *objmethods_gap,
633 garray_T *objmembers_gap)
634{
635 int success = TRUE;
636
637 for (int i = 0; i < impl_gap->ga_len && success; ++i)
638 {
639 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
640 typval_T tv;
641 tv.v_type = VAR_UNKNOWN;
642 if (eval_variable_import(impl, &tv) == FAIL)
643 {
644 semsg(_(e_interface_name_not_found_str), impl);
645 success = FALSE;
646 break;
647 }
648
649 if (tv.v_type != VAR_CLASS
650 || tv.vval.v_class == NULL
651 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
652 {
653 semsg(_(e_not_valid_interface_str), impl);
654 success = FALSE;
655 clear_tv(&tv);
656 break;
657 }
658
659 class_T *ifcl = tv.vval.v_class;
660 intf_classes[i] = ifcl;
661 ++ifcl->class_refcount;
662
663 // check the members of the interface match the members of the class
664 success = validate_interface_members(impl, ifcl, classmembers_gap,
665 objmembers_gap);
666
667 // check the functions/methods of the interface match the
668 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200669 if (success)
670 success = validate_interface_methods(impl, ifcl,
671 classfunctions_gap, objmethods_gap);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200672 clear_tv(&tv);
673 }
674
675 return success;
676}
677
678/*
679 * Check no function argument name is used as a class member.
680 * (Object members are always accessed with "this." prefix, so no need
681 * to check them.)
682 */
683 static int
684check_func_arg_names(
685 garray_T *classfunctions_gap,
686 garray_T *objmethods_gap,
687 garray_T *classmembers_gap)
688{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200689 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200690 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200691 {
692 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
693
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200694 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200695 {
696 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
697
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200698 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200699 {
700 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
701 garray_T *mgap = classmembers_gap;
702
703 // Check all the class member names
704 for (int mi = 0; mi < mgap->ga_len; ++mi)
705 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200706 char_u *mname =
707 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200708 if (STRCMP(aname, mname) == 0)
709 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200710 if (uf->uf_script_ctx.sc_sid > 0)
711 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
712
713 semsg(_(e_argument_already_declared_in_class_str),
714 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200715
716 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200717 }
718 }
719 }
720 }
721 }
722
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200723 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200724}
725
726/*
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200727 * Returns TRUE if the member "varname" is already defined.
728 */
729 static int
730is_duplicate_member(garray_T *mgap, char_u *varname, char_u *varname_end)
731{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200732 char_u *name = vim_strnsave(varname, varname_end - varname);
733 char_u *pstr = (*name == '_') ? name + 1 : name;
734 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200735
736 for (int i = 0; i < mgap->ga_len; ++i)
737 {
738 ocmember_T *m = ((ocmember_T *)mgap->ga_data) + i;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200739 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1 : m->ocm_name;
740 if (STRCMP(pstr, qstr) == 0)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200741 {
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200742 semsg(_(e_duplicate_member_str), name);
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200743 dup = TRUE;
744 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200745 }
746 }
747
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200748 vim_free(name);
749 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200750}
751
752/*
753 * Returns TRUE if the method "name" is already defined.
754 */
755 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200756is_duplicate_method(
757 garray_T *classmethods_gap,
758 garray_T *objmethods_gap,
759 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200760{
761 char_u *pstr = (*name == '_') ? name + 1 : name;
762
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200763 // loop 1: class methods, loop 2: object methods
764 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200765 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200766 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
767 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200768 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200769 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
770 char_u *qstr = *n == '_' ? n + 1 : n;
771 if (STRCMP(pstr, qstr) == 0)
772 {
773 semsg(_(e_duplicate_function_str), name);
774 return TRUE;
775 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200776 }
777 }
778
779 return FALSE;
780}
781
782/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200783 * Returns TRUE if the constructor is valid.
784 */
785 static int
786is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
787{
788 // Constructors are not allowed in abstract classes.
789 if (is_abstract)
790 {
791 emsg(_(e_cannot_define_new_function_in_abstract_class));
792 return FALSE;
793 }
794 // A constructor is always static, no need to define it so.
795 if (has_static)
796 {
797 emsg(_(e_cannot_define_new_function_as_static));
798 return FALSE;
799 }
800 // A return type should not be specified for the new()
801 // constructor method.
802 if (uf->uf_ret_type->tt_type != VAR_VOID)
803 {
804 emsg(_(e_cannot_use_a_return_type_with_new));
805 return FALSE;
806 }
807 return TRUE;
808}
809
810/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200811 * Update the interface class lookup table for the member index on the
812 * interface to the member index in the class implementing the interface.
813 * And a lookup table for the object method index on the interface
814 * to the object method index in the class implementing the interface.
815 * This is also used for updating the lookup table for the extended class
816 * hierarchy.
817 */
818 static int
819update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200820 class_T *ifcl,
821 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200822 garray_T *objmethods,
823 int pobj_method_offset,
824 int is_interface)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200825{
826 if (ifcl == NULL)
827 return OK;
828
829 // Table for members.
830 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200831 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200832 if (if2cl == NULL)
833 return FAIL;
834 if2cl->i2c_next = ifcl->class_itf2class;
835 ifcl->class_itf2class = if2cl;
836 if2cl->i2c_class = cl;
837 if2cl->i2c_is_method = FALSE;
838
839 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
840 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
841 {
842 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200843 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200844 {
845 int *table = (int *)(if2cl + 1);
846 table[if_i] = cl_i;
847 break;
848 }
849 }
850
851 // Table for methods.
852 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200853 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200854 if (if2cl == NULL)
855 return FAIL;
856 if2cl->i2c_next = ifcl->class_itf2class;
857 ifcl->class_itf2class = if2cl;
858 if2cl->i2c_class = cl;
859 if2cl->i2c_is_method = TRUE;
860
861 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
862 {
863 int done = FALSE;
864 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
865 {
866 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200867 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200868 {
869 int *table = (int *)(if2cl + 1);
870 table[if_i] = cl_i;
871 done = TRUE;
872 break;
873 }
874 }
875
876 // extended class object method is not overridden by the child class.
877 // Keep the method declared in one of the parent classes in the
878 // lineage.
879 if (!done && !is_interface)
880 {
881 // If "ifcl" is not the immediate parent of "cl", then search in
882 // the intermediate parent classes.
883 if (cl->class_extends != ifcl)
884 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200885 class_T *parent = cl->class_extends;
886 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200887
888 while (!done && parent != NULL && parent != ifcl)
889 {
890
891 for (int cl_i = 0;
892 cl_i < parent->class_obj_method_count_child; ++cl_i)
893 {
894 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
895 parent->class_obj_methods[cl_i]->uf_name)
896 == 0)
897 {
898 int *table = (int *)(if2cl + 1);
899 table[if_i] = method_offset + cl_i;
900 done = TRUE;
901 break;
902 }
903 }
904 method_offset += parent->class_obj_method_count_child;
905 parent = parent->class_extends;
906 }
907 }
908
909 if (!done)
910 {
911 int *table = (int *)(if2cl + 1);
912 table[if_i] = pobj_method_offset + if_i;
913 }
914 }
915 }
916
917 return OK;
918}
919
920/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200921 * Update the member and object method lookup tables for a new class in the
922 * interface class.
923 * For each interface add a lookup table for the member index on the interface
924 * to the member index in the new class. And a lookup table for the object
925 * method index on the interface to the object method index in the new class.
926 */
927 static int
928add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
929{
930 for (int i = 0; i < cl->class_interface_count; ++i)
931 {
932 class_T *ifcl = cl->class_interfaces_cl[i];
933
934 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
935 0, TRUE) == FAIL)
936 return FAIL;
937 }
938
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200939 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200940 if (extends_cl != NULL)
941 {
942 class_T *pclass = extends_cl;
943 int pobj_method_offset = objmethods_gap->ga_len;
944
945 // Update the entire lineage of extended classes.
946 while (pclass != NULL)
947 {
948 if (update_member_method_lookup_table(pclass, cl,
949 objmethods_gap, pobj_method_offset, FALSE) == FAIL)
950 return FAIL;
951
952 pobj_method_offset += pclass->class_obj_method_count_child;
953 pclass = pclass->class_extends;
954 }
955 }
956
957 return OK;
958}
959
960/*
961 * Add class members to a new class. Allocate a typval for each class member
962 * and initialize it.
963 */
964 static void
965add_class_members(class_T *cl, exarg_T *eap)
966{
967 // Allocate a typval for each class member and initialize it.
968 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
969 cl->class_class_member_count);
970 if (cl->class_members_tv == NULL)
971 return;
972
973 for (int i = 0; i < cl->class_class_member_count; ++i)
974 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200975 ocmember_T *m = &cl->class_class_members[i];
976 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200977 if (m->ocm_init != NULL)
978 {
979 typval_T *etv = eval_expr(m->ocm_init, eap);
980 if (etv != NULL)
981 {
982 *tv = *etv;
983 vim_free(etv);
984 }
985 }
986 else
987 {
988 // TODO: proper default value
989 tv->v_type = m->ocm_type->tt_type;
990 tv->vval.v_string = NULL;
991 }
992 }
993}
994
995/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +0200996 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200997 */
998 static void
999add_default_constructor(
1000 class_T *cl,
1001 garray_T *classfunctions_gap,
1002 garray_T *type_list_gap)
1003{
1004 garray_T fga;
1005
1006 ga_init2(&fga, 1, 1000);
1007 ga_concat(&fga, (char_u *)"new(");
1008 for (int i = 0; i < cl->class_obj_member_count; ++i)
1009 {
1010 if (i > 0)
1011 ga_concat(&fga, (char_u *)", ");
1012 ga_concat(&fga, (char_u *)"this.");
1013 ocmember_T *m = cl->class_obj_members + i;
1014 ga_concat(&fga, (char_u *)m->ocm_name);
1015 ga_concat(&fga, (char_u *)" = v:none");
1016 }
1017 ga_concat(&fga, (char_u *)")\nenddef\n");
1018 ga_append(&fga, NUL);
1019
1020 exarg_T fea;
1021 CLEAR_FIELD(fea);
1022 fea.cmdidx = CMD_def;
1023 fea.cmd = fea.arg = fga.ga_data;
1024
1025 garray_T lines_to_free;
1026 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1027
1028 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS);
1029
1030 ga_clear_strings(&lines_to_free);
1031 vim_free(fga.ga_data);
1032
1033 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1034 {
1035 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001036 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001037 ++classfunctions_gap->ga_len;
1038
1039 nf->uf_flags |= FC_NEW;
1040 nf->uf_ret_type = get_type_ptr(type_list_gap);
1041 if (nf->uf_ret_type != NULL)
1042 {
1043 nf->uf_ret_type->tt_type = VAR_OBJECT;
1044 nf->uf_ret_type->tt_class = cl;
1045 nf->uf_ret_type->tt_argcount = 0;
1046 nf->uf_ret_type->tt_args = NULL;
1047 }
1048 }
1049}
1050
1051/*
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001052 * Add the class methods and object methods to the new class "cl".
1053 * When extending a class "extends_cl", add the instance methods from the
1054 * parent class also.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001055 */
1056 static int
1057add_classfuncs_objmethods(
1058 class_T *cl,
1059 class_T *extends_cl,
1060 garray_T *classfunctions_gap,
1061 garray_T *objmethods_gap)
1062{
1063 // loop 1: class functions, loop 2: object methods
1064 for (int loop = 1; loop <= 2; ++loop)
1065 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001066 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1067 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001068 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001069 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001070 : &cl->class_obj_methods;
1071
1072 int parent_count = 0;
1073 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001074 // Include object methods from the parent.
1075 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001076 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001077 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001078 : extends_cl->class_obj_method_count;
1079
1080 *fcount = parent_count + gap->ga_len;
1081 if (*fcount == 0)
1082 {
1083 *fup = NULL;
1084 continue;
1085 }
1086 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1087 if (*fup == NULL)
1088 return FAIL;
1089
1090 if (gap->ga_len != 0)
1091 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
1092 vim_free(gap->ga_data);
1093 if (loop == 1)
1094 cl->class_class_function_count_child = gap->ga_len;
1095 else
1096 cl->class_obj_method_count_child = gap->ga_len;
1097
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001098 if (loop == 2)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001099 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001100 // Copy instance methods from the parent.
1101
1102 for (int i = 0; i < parent_count; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001103 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001104 // Can't use the same parent function, because "uf_class" is
1105 // different and compilation will have a different result.
1106 // Put them after the functions in the current class, object
1107 // methods may be overruled, then "super.Method()" is used to
1108 // find a method from the parent.
1109 ufunc_T *pf = (extends_cl->class_obj_methods)[i];
1110 (*fup)[gap->ga_len + i] = copy_function(pf);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001111
1112 // If the child class overrides a function from the parent
1113 // the signature must be equal.
1114 char_u *pname = pf->uf_name;
1115 for (int ci = 0; ci < gap->ga_len; ++ci)
1116 {
1117 ufunc_T *cf = (*fup)[ci];
1118 char_u *cname = cf->uf_name;
1119 if (STRCMP(pname, cname) == 0)
1120 {
1121 where_T where = WHERE_INIT;
1122 where.wt_func_name = (char *)pname;
1123 where.wt_kind = WT_METHOD;
1124 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1125 TRUE, where);
1126 }
1127 }
1128 }
1129 }
1130
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001131 // Set the class pointer on all the functions and object methods.
1132 for (int i = 0; i < *fcount; ++i)
1133 {
1134 ufunc_T *fp = (*fup)[i];
1135 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001136 if (i < gap->ga_len)
1137 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001138 if (loop == 2)
1139 fp->uf_flags |= FC_OBJECT;
1140 }
1141 }
1142
1143 return OK;
1144}
1145
1146/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001147 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001148 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001149 */
1150 void
1151ex_class(exarg_T *eap)
1152{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001153 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1154 long start_lnum = SOURCING_LNUM;
1155 char_u *arg = eap->arg;
1156 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001157
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001158 if (is_abstract)
1159 {
1160 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1161 {
1162 semsg(_(e_invalid_argument_str), arg);
1163 return;
1164 }
1165 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001166 is_class = TRUE;
1167 }
1168
1169 if (!current_script_is_vim9()
1170 || (cmdmod.cmod_flags & CMOD_LEGACY)
1171 || !getline_equal(eap->getline, eap->cookie, getsourceline))
1172 {
1173 if (is_class)
1174 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1175 else
1176 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1177 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001178 }
1179
1180 if (!ASCII_ISUPPER(*arg))
1181 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001182 if (is_class)
1183 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1184 else
1185 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1186 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001187 return;
1188 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001189 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1190 if (!IS_WHITE_OR_NUL(*name_end))
1191 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001192 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001193 return;
1194 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001195 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001196
Bram Moolenaara86655a2023-01-12 17:06:27 +00001197 // "export class" gets used when creating the class, don't use "is_export"
1198 // for the items inside the class.
1199 int class_export = is_export;
1200 is_export = FALSE;
1201
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001202 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001203 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001204
Bram Moolenaar83677162023-01-08 19:54:10 +00001205 // Name for "extends BaseClass"
1206 char_u *extends = NULL;
1207
Bram Moolenaar94674f22023-01-06 18:42:20 +00001208 // Names for "implements SomeInterface"
1209 garray_T ga_impl;
1210 ga_init2(&ga_impl, sizeof(char_u *), 5);
1211
1212 arg = skipwhite(name_end);
1213 while (*arg != NUL && *arg != '#' && *arg != '\n')
1214 {
1215 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001216 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001217 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1218 {
1219 if (extends != NULL)
1220 {
1221 emsg(_(e_duplicate_extends));
1222 goto early_ret;
1223 }
1224 arg = skipwhite(arg + 7);
1225 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1226 if (!IS_WHITE_OR_NUL(*end))
1227 {
1228 semsg(_(e_white_space_required_after_name_str), arg);
1229 goto early_ret;
1230 }
1231 extends = vim_strnsave(arg, end - arg);
1232 if (extends == NULL)
1233 goto early_ret;
1234
1235 arg = skipwhite(end + 1);
1236 }
1237 else if (STRNCMP(arg, "implements", 10) == 0
1238 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001239 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001240 if (ga_impl.ga_len > 0)
1241 {
1242 emsg(_(e_duplicate_implements));
1243 goto early_ret;
1244 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001245 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001246
1247 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001248 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001249 char_u *impl_end = find_name_end(arg, NULL, NULL,
1250 FNE_CHECK_START);
1251 if (!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1252 {
1253 semsg(_(e_white_space_required_after_name_str), arg);
1254 goto early_ret;
1255 }
1256 char_u *iname = vim_strnsave(arg, impl_end - arg);
1257 if (iname == NULL)
1258 goto early_ret;
1259 for (int i = 0; i < ga_impl.ga_len; ++i)
1260 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1261 {
1262 semsg(_(e_duplicate_interface_after_implements_str),
1263 iname);
1264 vim_free(iname);
1265 goto early_ret;
1266 }
1267 if (ga_add_string(&ga_impl, iname) == FAIL)
1268 {
1269 vim_free(iname);
1270 goto early_ret;
1271 }
1272 if (*impl_end != ',')
1273 {
1274 arg = skipwhite(impl_end);
1275 break;
1276 }
1277 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001278 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001279 }
1280 else
1281 {
1282 semsg(_(e_trailing_characters_str), arg);
1283early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001284 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001285 ga_clear_strings(&ga_impl);
1286 return;
1287 }
1288 }
1289
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001290 garray_T type_list; // list of pointers to allocated types
1291 ga_init2(&type_list, sizeof(type_T *), 10);
1292
Bram Moolenaard505d172022-12-18 21:42:55 +00001293 // Growarray with class members declared in the class.
1294 garray_T classmembers;
1295 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1296
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001297 // Growarray with functions declared in the class.
1298 garray_T classfunctions;
1299 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001300
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001301 // Growarray with object members declared in the class.
1302 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001303 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001304
1305 // Growarray with object methods declared in the class.
1306 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001307 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001308
1309 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001310 * Go over the body of the class/interface until "endclass" or
1311 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001312 */
1313 char_u *theline = NULL;
1314 int success = FALSE;
1315 for (;;)
1316 {
1317 vim_free(theline);
1318 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
1319 if (theline == NULL)
1320 break;
1321 char_u *line = skipwhite(theline);
1322
Bram Moolenaar418b5472022-12-20 13:38:22 +00001323 // Skip empty and comment lines.
1324 if (*line == NUL)
1325 continue;
1326 if (*line == '#')
1327 {
1328 if (vim9_bad_comment(line))
1329 break;
1330 continue;
1331 }
1332
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001333 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001334 char *end_name = is_class ? "endclass" : "endinterface";
1335 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001336 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001337 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001338 semsg(_(e_command_cannot_be_shortened_str), line);
1339 else if (*p == '|' || !ends_excmd2(line, p))
1340 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001341 else
1342 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001343 break;
1344 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001345 char *wrong_name = is_class ? "endinterface" : "endclass";
1346 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1347 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001348 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001349 break;
1350 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001351
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001352 int has_public = FALSE;
1353 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001354 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001355 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001356 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001357 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001358 break;
1359 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001360 has_public = TRUE;
1361 p = skipwhite(line + 6);
1362
Bram Moolenaard505d172022-12-18 21:42:55 +00001363 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001364 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001365 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001366 break;
1367 }
1368 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001369
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001370 int abstract_method = FALSE;
1371 char_u *pa = p;
1372 if (checkforcmd(&p, "abstract", 3))
1373 {
1374 if (STRNCMP(pa, "abstract", 8) != 0)
1375 {
1376 semsg(_(e_command_cannot_be_shortened_str), pa);
1377 break;
1378 }
1379
1380 if (!is_abstract)
1381 {
1382 semsg(_(e_abstract_method_in_concrete_class), pa);
1383 break;
1384 }
1385
1386 abstract_method = TRUE;
1387 p = skipwhite(pa + 8);
1388 if (STRNCMP(p, "def", 3) != 0 && STRNCMP(p, "static", 6) != 0)
1389 {
1390 emsg(_(e_abstract_must_be_followed_by_def_or_static));
1391 break;
1392 }
1393 }
1394
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001395 int has_static = FALSE;
1396 char_u *ps = p;
1397 if (checkforcmd(&p, "static", 4))
1398 {
1399 if (STRNCMP(ps, "static", 6) != 0)
1400 {
1401 semsg(_(e_command_cannot_be_shortened_str), ps);
1402 break;
1403 }
1404 has_static = TRUE;
1405 p = skipwhite(ps + 6);
1406 }
1407
Bram Moolenaard505d172022-12-18 21:42:55 +00001408 // object members (public, read access, private):
1409 // "this._varname"
1410 // "this.varname"
1411 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001412 if (STRNCMP(p, "this", 4) == 0)
1413 {
1414 if (p[4] != '.' || !eval_isnamec1(p[5]))
1415 {
1416 semsg(_(e_invalid_object_member_declaration_str), p);
1417 break;
1418 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001419 if (has_static)
1420 {
1421 emsg(_(e_static_cannot_be_followed_by_this));
1422 break;
1423 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001424 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +00001425 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001426 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001427 char_u *init_expr = NULL;
1428 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001429 &varname_end, &type_list, &type,
1430 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001431 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001432 if (is_duplicate_member(&objmembers, varname, varname_end))
1433 {
1434 vim_free(init_expr);
1435 break;
1436 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001437 if (add_member(&objmembers, varname, varname_end,
1438 has_public, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001439 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001440 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001441 break;
1442 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001443 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001444
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001445 // constructors:
1446 // def new()
1447 // enddef
1448 // def newOther()
1449 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001450 // object methods and class functions:
1451 // def SomeMethod()
1452 // enddef
1453 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001454 // enddef
1455 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001456 // def <Tval> someMethod()
1457 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001458 else if (checkforcmd(&p, "def", 3))
1459 {
1460 exarg_T ea;
1461 garray_T lines_to_free;
1462
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001463 // TODO: error for "public static def Func()"?
1464
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001465 CLEAR_FIELD(ea);
1466 ea.cmd = line;
1467 ea.arg = p;
1468 ea.cmdidx = CMD_def;
1469 ea.getline = eap->getline;
1470 ea.cookie = eap->cookie;
1471
1472 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001473 int class_flags;
1474 if (is_class)
1475 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
1476 else
1477 class_flags = CF_INTERFACE;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001478 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001479 class_flags);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001480 ga_clear_strings(&lines_to_free);
1481
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001482 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001483 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001484 char_u *name = uf->uf_name;
1485 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001486
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001487 if (is_new && !is_valid_constructor(uf, is_abstract,
1488 has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001489 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001490 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001491 break;
1492 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001493
Bram Moolenaar58b40092023-01-11 15:59:05 +00001494 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001495 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001496 {
1497 success = FALSE;
1498 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001499 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001500 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001501
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001502 garray_T *fgap = has_static || is_new
1503 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001504 if (ga_grow(fgap, 1) == OK)
1505 {
1506 if (is_new)
1507 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001508
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001509 if (abstract_method)
1510 uf->uf_flags |= FC_ABSTRACT;
1511
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001512 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1513 ++fgap->ga_len;
1514 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001515 }
1516 }
1517
1518 // class members
1519 else if (has_static)
1520 {
1521 // class members (public, read access, private):
1522 // "static _varname"
1523 // "static varname"
1524 // "public static varname"
1525 char_u *varname = p;
1526 char_u *varname_end = NULL;
1527 type_T *type = NULL;
1528 char_u *init_expr = NULL;
1529 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001530 &varname_end, &type_list, &type,
1531 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001532 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001533 if (is_duplicate_member(&classmembers, varname, varname_end))
1534 {
1535 vim_free(init_expr);
1536 break;
1537 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001538 if (add_member(&classmembers, varname, varname_end,
1539 has_public, type, init_expr) == FAIL)
1540 {
1541 vim_free(init_expr);
1542 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001543 }
1544 }
1545
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001546 else
1547 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001548 if (is_class)
1549 semsg(_(e_not_valid_command_in_class_str), line);
1550 else
1551 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001552 break;
1553 }
1554 }
1555 vim_free(theline);
1556
Bram Moolenaar83677162023-01-08 19:54:10 +00001557 class_T *extends_cl = NULL; // class from "extends" argument
1558
1559 /*
1560 * Check a few things before defining the class.
1561 */
1562
1563 // Check the "extends" class is valid.
1564 if (success && extends != NULL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001565 success = validate_extends_class(extends, &extends_cl);
Bram Moolenaar83677162023-01-08 19:54:10 +00001566 VIM_CLEAR(extends);
1567
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001568 // Check the new object methods to make sure their access (public or
1569 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001570 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001571 success = validate_extends_methods(&objmethods, extends_cl);
1572
1573 // Check the new class and object variables are not duplicates of the
1574 // variables in the extended class lineage.
1575 if (success && extends_cl != NULL)
1576 success = validate_extends_members(&objmembers, extends_cl);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001577
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001578 // When extending an abstract class, make sure all the abstract methods in
1579 // the parent class are implemented. If the current class is an abstract
1580 // class, then there is no need for this check.
1581 if (success && !is_abstract && extends_cl != NULL
1582 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001583 success = validate_abstract_class_methods(&classfunctions,
1584 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001585
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001586 class_T **intf_classes = NULL;
1587
Bram Moolenaar83677162023-01-08 19:54:10 +00001588 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001589 if (success && ga_impl.ga_len > 0)
1590 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001591 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1592
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001593 success = validate_implements_classes(&ga_impl, intf_classes,
1594 &classfunctions, &classmembers,
1595 &objmethods, &objmembers);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001596 }
1597
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001598 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001599 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001600 success = check_func_arg_names(&classfunctions, &objmethods,
1601 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001602
Bram Moolenaareb533502022-12-14 15:06:11 +00001603 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001604 if (success)
1605 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001606 // "endclass" encountered without failures: Create the class.
1607
Bram Moolenaareb533502022-12-14 15:06:11 +00001608 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001609 if (cl == NULL)
1610 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001611 if (!is_class)
1612 cl->class_flags = CLASS_INTERFACE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001613 else if (is_abstract)
1614 cl->class_flags = CLASS_ABSTRACT;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001615
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001616 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001617 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001618 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001619 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001620
Bram Moolenaard0200c82023-01-28 15:19:40 +00001621 if (extends_cl != NULL)
1622 {
1623 cl->class_extends = extends_cl;
1624 extends_cl->class_flags |= CLASS_EXTENDED;
1625 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001626
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001627 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00001628 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001629 NULL,
1630 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00001631 &cl->class_class_members,
1632 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001633 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001634 extends_cl == NULL ? NULL
1635 : extends_cl->class_obj_members,
1636 extends_cl == NULL ? 0
1637 : extends_cl->class_obj_member_count,
1638 &cl->class_obj_members,
1639 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001640 goto cleanup;
1641
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001642 if (ga_impl.ga_len > 0)
1643 {
1644 // Move the "implements" names into the class.
1645 cl->class_interface_count = ga_impl.ga_len;
1646 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1647 if (cl->class_interfaces == NULL)
1648 goto cleanup;
1649 for (int i = 0; i < ga_impl.ga_len; ++i)
1650 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1651 VIM_CLEAR(ga_impl.ga_data);
1652 ga_impl.ga_len = 0;
1653
Bram Moolenaard0200c82023-01-28 15:19:40 +00001654 cl->class_interfaces_cl = intf_classes;
1655 intf_classes = NULL;
1656 }
1657
1658 if (cl->class_interface_count > 0 || extends_cl != NULL)
1659 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001660 // Add a method and member lookup table to each of the interface
1661 // classes.
1662 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1663 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001664 }
1665
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001666 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001667 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001668 add_class_members(cl, eap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001669
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001670 int have_new = FALSE;
1671 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001672 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001673 {
1674 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1675 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001676 {
1677 have_new = TRUE;
1678 break;
1679 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001680 }
1681
1682 if (have_new)
1683 // The return type of new() is an object of class "cl"
1684 class_func->uf_ret_type->tt_class = cl;
1685 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001686 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001687 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001688
Bram Moolenaar58b40092023-01-11 15:59:05 +00001689 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001690 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1691 &objmethods) == FAIL)
1692 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001693
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001694 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001695 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001696 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001697 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001698 cl->class_type_list = type_list;
1699
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001700 class_created(cl);
1701
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001702 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001703 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001704
1705 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001706 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02001707 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001708 typval_T tv;
1709 tv.v_type = VAR_CLASS;
1710 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001711 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001712 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001713 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001714 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001715 return;
1716 }
1717
1718cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00001719 if (cl != NULL)
1720 {
1721 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001722 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001723 if (cl->class_interfaces != NULL)
1724 {
1725 for (int i = 0; i < cl->class_interface_count; ++i)
1726 vim_free(cl->class_interfaces[i]);
1727 vim_free(cl->class_interfaces);
1728 }
1729 if (cl->class_interfaces_cl != NULL)
1730 {
1731 for (int i = 0; i < cl->class_interface_count; ++i)
1732 class_unref(cl->class_interfaces_cl[i]);
1733 vim_free(cl->class_interfaces_cl);
1734 }
Bram Moolenaareb533502022-12-14 15:06:11 +00001735 vim_free(cl->class_obj_members);
1736 vim_free(cl->class_obj_methods);
1737 vim_free(cl);
1738 }
1739
Bram Moolenaar83677162023-01-08 19:54:10 +00001740 vim_free(extends);
1741 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001742
1743 if (intf_classes != NULL)
1744 {
1745 for (int i = 0; i < ga_impl.ga_len; ++i)
1746 class_unref(intf_classes[i]);
1747 vim_free(intf_classes);
1748 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001749 ga_clear_strings(&ga_impl);
1750
Bram Moolenaard505d172022-12-18 21:42:55 +00001751 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001752 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001753 garray_T *gap = round == 1 ? &classmembers : &objmembers;
1754 if (gap->ga_len == 0 || gap->ga_data == NULL)
1755 continue;
1756
1757 for (int i = 0; i < gap->ga_len; ++i)
1758 {
1759 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
1760 vim_free(m->ocm_name);
1761 vim_free(m->ocm_init);
1762 }
1763 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001764 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001765
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001766 for (int i = 0; i < objmethods.ga_len; ++i)
1767 {
1768 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
1769 func_clear_free(uf, FALSE);
1770 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001771 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001772
1773 for (int i = 0; i < classfunctions.ga_len; ++i)
1774 {
1775 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
1776 func_clear_free(uf, FALSE);
1777 }
1778 ga_clear(&classfunctions);
1779
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001780 clear_type_list(&type_list);
1781}
1782
1783/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001784 * Find member "name" in class "cl", set "member_idx" to the member index and
1785 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001786 * When "is_object" is TRUE, then look for object members. Otherwise look for
1787 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001788 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02001789 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001790 */
1791 type_T *
1792class_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001793 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001794 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001795 char_u *name,
1796 char_u *name_end,
1797 int *member_idx,
Ernie Rael456ae552023-09-01 18:54:54 +02001798 ocmember_T **p_m)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001799{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001800 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001801 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001802
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001803 *member_idx = -1; // not found (yet)
1804
1805 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
1806 member_idx);
1807 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001808 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001809 char_u *varname = vim_strnsave(name, len);
1810 if (varname != NULL)
1811 {
1812 if (is_object && class_member_idx(cl, name, len) >= 0)
1813 // A class variable with this name is present
1814 semsg(_(e_class_member_str_accessible_only_inside_class_str),
1815 varname, cl->class_name);
1816 else if (!is_object && object_member_idx(cl, name, len) >= 0)
1817 // An instance variable with this name is present
1818 semsg(_(e_object_member_str_accessible_only_using_object_str),
1819 varname, cl->class_name);
1820 else
1821 semsg(_(e_unknown_variable_str), varname);
1822 }
1823 vim_free(varname);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001824 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001825 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001826
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001827 if (p_m != NULL)
1828 *p_m = m;
1829
1830 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001831}
1832
1833/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001834 * Handle ":enum" up to ":endenum".
1835 */
1836 void
1837ex_enum(exarg_T *eap UNUSED)
1838{
1839 // TODO
1840}
1841
1842/*
1843 * Handle ":type".
1844 */
1845 void
1846ex_type(exarg_T *eap UNUSED)
1847{
1848 // TODO
1849}
1850
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001851/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001852 * Returns OK if a member variable named "name" is present in the class "cl".
1853 * Otherwise returns FAIL. If found, the member variable typval is set in
1854 * "rettv". If "is_object" is TRUE, then the object member variable table is
1855 * searched. Otherwise the class member variable table is searched.
1856 */
1857 static int
1858get_member_tv(
1859 class_T *cl,
1860 int is_object,
1861 char_u *name,
1862 size_t namelen,
1863 typval_T *rettv)
1864{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001865 ocmember_T *m;
1866 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001867
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001868 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
1869 &m_idx);
1870 if (m == NULL)
1871 return FAIL;
1872
1873 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001874 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001875 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
1876 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001877 }
1878
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001879 // The object only contains a pointer to the class, the member
1880 // values array follows right after that.
1881 object_T *obj = rettv->vval.v_object;
1882 if (is_object)
1883 {
1884 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
1885 copy_tv(tv, rettv);
1886 }
1887 else
1888 copy_tv(&cl->class_members_tv[m_idx], rettv);
1889
1890 object_unref(obj);
1891
1892 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001893}
1894
1895/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001896 * Evaluate what comes after a class:
1897 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001898 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001899 * - class constructor: SomeClass.new()
1900 * - object member: someObject.varname
1901 * - object method: someObject.SomeMethod()
1902 *
1903 * "*arg" points to the '.'.
1904 * "*arg" is advanced to after the member name or method call.
1905 *
1906 * Returns FAIL or OK.
1907 */
1908 int
1909class_object_index(
1910 char_u **arg,
1911 typval_T *rettv,
1912 evalarg_T *evalarg,
1913 int verbose UNUSED) // give error messages
1914{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001915 if (VIM_ISWHITE((*arg)[1]))
1916 {
1917 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
1918 return FAIL;
1919 }
1920
1921 ++*arg;
1922 char_u *name = *arg;
1923 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
1924 if (name_end == name)
1925 return FAIL;
1926 size_t len = name_end - name;
1927
Bram Moolenaar552bdca2023-02-17 21:08:50 +00001928 class_T *cl;
1929 if (rettv->v_type == VAR_CLASS)
1930 cl = rettv->vval.v_class;
1931 else // VAR_OBJECT
1932 {
1933 if (rettv->vval.v_object == NULL)
1934 {
1935 emsg(_(e_using_null_object));
1936 return FAIL;
1937 }
1938 cl = rettv->vval.v_object->obj_class;
1939 }
1940
Bram Moolenaard13dd302023-03-11 20:56:35 +00001941 if (cl == NULL)
1942 {
1943 emsg(_(e_incomplete_type));
1944 return FAIL;
1945 }
1946
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001947 if (*name_end == '(')
1948 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001949 ufunc_T *fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001950
Ernie Rael4d00b832023-09-11 19:54:42 +02001951 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001952 if (fp == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001953 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001954 method_not_found_msg(cl, rettv->v_type, name, len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001955 return FAIL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001956 }
1957
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02001958 typval_T argvars[MAX_FUNC_ARGS + 1];
1959 int argcount = 0;
1960
1961 if (*fp->uf_name == '_')
1962 {
1963 // Cannot access a private method outside of a class
1964 semsg(_(e_cannot_access_private_method_str), name);
1965 return FAIL;
1966 }
1967
1968 char_u *argp = name_end;
1969 int ret = get_func_arguments(&argp, evalarg, 0,
1970 argvars, &argcount);
1971 if (ret == FAIL)
1972 return FAIL;
1973
1974 funcexe_T funcexe;
1975 CLEAR_FIELD(funcexe);
1976 funcexe.fe_evaluate = TRUE;
1977 if (rettv->v_type == VAR_OBJECT)
1978 {
1979 funcexe.fe_object = rettv->vval.v_object;
1980 ++funcexe.fe_object->obj_refcount;
1981 }
1982
1983 // Clear the class or object after calling the function, in
1984 // case the refcount is one.
1985 typval_T tv_tofree = *rettv;
1986 rettv->v_type = VAR_UNKNOWN;
1987
1988 // Call the user function. Result goes into rettv;
1989 int error = call_user_func_check(fp, argcount, argvars,
1990 rettv, &funcexe, NULL);
1991
1992 // Clear the previous rettv and the arguments.
1993 clear_tv(&tv_tofree);
1994 for (int idx = 0; idx < argcount; ++idx)
1995 clear_tv(&argvars[idx]);
1996
1997 if (error != FCERR_NONE)
1998 {
1999 user_func_error(error, printable_func_name(fp),
2000 funcexe.fe_found_var);
2001 return FAIL;
2002 }
2003 *arg = argp;
2004 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002005 }
2006
2007 else if (rettv->v_type == VAR_OBJECT)
2008 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002009 // Search in the object member variable table and the class member
2010 // variable table.
Yegappan Lakshmanan23c92d92023-09-09 11:33:29 +02002011 if (get_member_tv(cl, TRUE, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002012 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002013 *arg = name_end;
2014 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002015 }
2016
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002017 member_not_found_msg(cl, VAR_OBJECT, name, len);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002018 }
2019
Bram Moolenaard505d172022-12-18 21:42:55 +00002020 else if (rettv->v_type == VAR_CLASS)
2021 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002022 int m_idx;
2023
Bram Moolenaard505d172022-12-18 21:42:55 +00002024 // class member
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002025 ocmember_T *m = class_member_lookup(cl, name, len, &m_idx);
2026 if (m == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002027 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002028 member_not_found_msg(cl, VAR_CLASS, name, len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002029 return FAIL;
Bram Moolenaard505d172022-12-18 21:42:55 +00002030 }
2031
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002032 if (*name == '_')
2033 {
2034 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
2035 return FAIL;
2036 }
2037 if ((cl->class_flags & CLASS_INTERFACE) != 0)
2038 {
2039 semsg(_(e_interface_static_direct_access_str),
2040 cl->class_name, m->ocm_name);
2041 return FAIL;
2042 }
2043
2044 typval_T *tv = &cl->class_members_tv[m_idx];
2045 copy_tv(tv, rettv);
2046 class_unref(cl);
2047
2048 *arg = name_end;
2049 return OK;
Bram Moolenaard505d172022-12-18 21:42:55 +00002050 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002051
2052 return FAIL;
2053}
2054
2055/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002056 * If "arg" points to a class or object method, return it.
2057 * Otherwise return NULL.
2058 */
2059 ufunc_T *
2060find_class_func(char_u **arg)
2061{
2062 char_u *name = *arg;
2063 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2064 if (name_end == name || *name_end != '.')
2065 return NULL;
2066
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002067 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002068 size_t len = name_end - name;
2069 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002070 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00002071 if (eval_variable(name, (int)len,
2072 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002073 return NULL;
2074 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00002075 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002076
2077 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
2078 : tv.vval.v_object->obj_class;
2079 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002080 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002081 char_u *fname = name_end + 1;
2082 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
2083 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00002084 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002085 len = fname_end - fname;
2086
Ernie Rael4d00b832023-09-11 19:54:42 +02002087 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002088
Bram Moolenaareb533502022-12-14 15:06:11 +00002089fail_after_eval:
2090 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002091 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002092}
2093
2094/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002095 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002096 * Returns -1, if the variable is not found.
2097 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002098 */
2099 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002100class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002101{
Ernie Rael4d00b832023-09-11 19:54:42 +02002102 int idx;
2103 class_member_lookup(cl, name, namelen, &idx);
2104 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002105}
2106
2107/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002108 * Returns a pointer to the class member variable "name" in the class "cl".
2109 * Returns NULL if the variable is not found.
2110 * The member variable index is set in "idx".
2111 */
2112 ocmember_T *
2113class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2114{
Ernie Rael4d00b832023-09-11 19:54:42 +02002115 ocmember_T *ret_m = NULL;
2116 int ret_idx = -1;
2117 for (int i = 0; i < cl->class_class_member_count; ++i)
2118 {
2119 ocmember_T *m = &cl->class_class_members[i];
2120 if (namelen)
2121 {
2122 if (STRNCMP(name, m->ocm_name, namelen) == 0
2123 && m->ocm_name[namelen] == NUL)
2124 {
2125 ret_m = m;
2126 ret_idx = i;
2127 break;
2128 }
2129 }
2130 else if (STRCMP(name, m->ocm_name) == 0)
2131 {
2132 ret_m = m;
2133 ret_idx = i;
2134 break;
2135 }
2136 }
2137 if (idx != NULL)
2138 *idx = ret_idx;
2139 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002140}
2141
2142/*
2143 * Returns the index of class method "name" in the class "cl".
2144 * Returns -1, if the method is not found.
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002145 */
2146 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002147class_method_idx(class_T *cl, char_u *name, size_t namelen)
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002148{
Ernie Rael4d00b832023-09-11 19:54:42 +02002149 int idx;
2150 class_method_lookup(cl, name, namelen, &idx);
2151 return idx;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002152}
2153
2154/*
2155 * Returns a pointer to the class method "name" in class "cl".
2156 * Returns NULL if the method is not found.
2157 * The method index is set in "idx".
2158 */
2159 ufunc_T *
2160class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2161{
Ernie Rael4d00b832023-09-11 19:54:42 +02002162 ufunc_T *ret_fp = NULL;
2163 int ret_idx = -1;
2164 for (int i = 0; i < cl->class_class_function_count; ++i)
2165 {
2166 ufunc_T *fp = cl->class_class_functions[i];
2167 char_u *ufname = (char_u *)fp->uf_name;
2168 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2169 {
2170 ret_fp = fp;
2171 ret_idx = i;
2172 break;
2173 }
2174 }
2175 if (idx != NULL)
2176 *idx = ret_idx;
2177 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002178}
2179
2180/*
2181 * Returns the index of object member variable "name" in the class "cl".
2182 * Returns -1, if the variable is not found.
2183 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
2184 */
2185 int
2186object_member_idx(class_T *cl, char_u *name, size_t namelen)
2187{
Ernie Rael4d00b832023-09-11 19:54:42 +02002188 int idx;
2189 object_member_lookup(cl, name, namelen, &idx);
2190 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002191}
2192
2193/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002194 * Returns a pointer to the object member variable "name" in the class "cl".
2195 * Returns NULL if the variable is not found.
2196 * The object member variable index is set in "idx".
2197 */
2198 ocmember_T *
2199object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2200{
Ernie Rael4d00b832023-09-11 19:54:42 +02002201 ocmember_T *ret_m = NULL;
2202 int ret_idx = -1;
2203 for (int i = 0; i < cl->class_obj_member_count; ++i)
2204 {
2205 ocmember_T *m = &cl->class_obj_members[i];
2206 if (namelen)
2207 {
2208 if (STRNCMP(name, m->ocm_name, namelen) == 0
2209 && m->ocm_name[namelen] == NUL)
2210 {
2211 ret_m = m;
2212 ret_idx = i;
2213 break;
2214 }
2215 }
2216 else if (STRCMP(name, m->ocm_name) == 0)
2217 {
2218 ret_m = m;
2219 ret_idx = i;
2220 break;
2221 }
2222 }
2223 if (idx != NULL)
2224 *idx = ret_idx;
2225 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002226}
2227
2228/*
2229 * Returns the index of object method "name" in the class "cl".
2230 * Returns -1, if the method is not found.
2231 */
2232 int
2233object_method_idx(class_T *cl, char_u *name, size_t namelen)
2234{
Ernie Rael4d00b832023-09-11 19:54:42 +02002235 int idx;
2236 object_method_lookup(cl, name, namelen, &idx);
2237 return idx;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002238}
2239
2240/*
2241 * Returns a pointer to the object method "name" in class "cl".
2242 * Returns NULL if the method is not found.
2243 * The object method index is set in "idx".
2244 */
2245 ufunc_T *
2246object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2247{
Ernie Rael4d00b832023-09-11 19:54:42 +02002248 ufunc_T *ret_fp = NULL;
2249 int ret_idx = -1;
2250 for (int i = 0; i < cl->class_obj_method_count; ++i)
2251 {
2252 ufunc_T *fp = cl->class_obj_methods[i];
2253 // Use a separate pointer to avoid that ASAN complains about
2254 // uf_name[] only being 4 characters.
2255 char_u *ufname = (char_u *)fp->uf_name;
2256 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2257 {
2258 ret_fp = fp;
2259 ret_idx = i;
2260 break;
2261 }
2262 }
2263 if (idx != NULL)
2264 *idx = ret_idx;
2265 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002266}
2267
2268/*
2269 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
2270 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
2271 * object member variable.
2272 *
2273 * Returns a pointer to the member variable structure if variable is found.
2274 * Otherwise returns NULL. The member variable index is set in "*idx".
2275 */
2276 ocmember_T *
2277member_lookup(
2278 class_T *cl,
2279 vartype_T v_type,
2280 char_u *name,
2281 size_t namelen,
2282 int *idx)
2283{
2284 if (v_type == VAR_CLASS)
2285 return class_member_lookup(cl, name, namelen, idx);
2286 else
2287 return object_member_lookup(cl, name, namelen, idx);
2288}
2289
2290/*
2291 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
2292 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
2293 *
2294 * Returns a pointer to the method structure if variable is found.
2295 * Otherwise returns NULL. The method variable index is set in "*idx".
2296 */
2297 ufunc_T *
2298method_lookup(
2299 class_T *cl,
2300 vartype_T v_type,
2301 char_u *name,
2302 size_t namelen,
2303 int *idx)
2304{
2305 if (v_type == VAR_CLASS)
2306 return class_method_lookup(cl, name, namelen, idx);
2307 else
2308 return object_method_lookup(cl, name, namelen, idx);
2309}
2310
2311/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00002312 * Return TRUE if current context "cctx_arg" is inside class "cl".
2313 * Return FALSE if not.
2314 */
2315 int
2316inside_class(cctx_T *cctx_arg, class_T *cl)
2317{
2318 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002319 if (cctx->ctx_ufunc != NULL
2320 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002321 return TRUE;
2322 return FALSE;
2323}
2324
2325/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002326 * Make a copy of an object.
2327 */
2328 void
2329copy_object(typval_T *from, typval_T *to)
2330{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002331 if (from->vval.v_object == NULL)
2332 to->vval.v_object = NULL;
2333 else
2334 {
2335 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002336 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002337 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002338}
2339
2340/*
2341 * Free an object.
2342 */
2343 static void
2344object_clear(object_T *obj)
2345{
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002346 // Avoid a recursive call, it can happen if "obj" has a circular reference.
2347 obj->obj_refcount = INT_MAX;
2348
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002349 class_T *cl = obj->obj_class;
2350
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002351 if (!cl)
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002352 return;
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02002353
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002354 // the member values are just after the object structure
2355 typval_T *tv = (typval_T *)(obj + 1);
2356 for (int i = 0; i < cl->class_obj_member_count; ++i)
2357 clear_tv(tv + i);
2358
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002359 // Remove from the list headed by "first_object".
2360 object_cleared(obj);
2361
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002362 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002363 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002364}
2365
2366/*
2367 * Unreference an object.
2368 */
2369 void
2370object_unref(object_T *obj)
2371{
2372 if (obj != NULL && --obj->obj_refcount <= 0)
2373 object_clear(obj);
2374}
2375
2376/*
2377 * Make a copy of a class.
2378 */
2379 void
2380copy_class(typval_T *from, typval_T *to)
2381{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002382 if (from->vval.v_class == NULL)
2383 to->vval.v_class = NULL;
2384 else
2385 {
2386 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002387 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002388 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002389}
2390
2391/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002392 * Free the class "cl" and its contents.
2393 */
2394 static void
2395class_free(class_T *cl)
2396{
2397 // Freeing what the class contains may recursively come back here.
2398 // Clear "class_name" first, if it is NULL the class does not need to
2399 // be freed.
2400 VIM_CLEAR(cl->class_name);
2401
2402 class_unref(cl->class_extends);
2403
2404 for (int i = 0; i < cl->class_interface_count; ++i)
2405 {
2406 vim_free(((char_u **)cl->class_interfaces)[i]);
2407 if (cl->class_interfaces_cl[i] != NULL)
2408 class_unref(cl->class_interfaces_cl[i]);
2409 }
2410 vim_free(cl->class_interfaces);
2411 vim_free(cl->class_interfaces_cl);
2412
2413 itf2class_T *next;
2414 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2415 {
2416 next = i2c->i2c_next;
2417 vim_free(i2c);
2418 }
2419
2420 for (int i = 0; i < cl->class_class_member_count; ++i)
2421 {
2422 ocmember_T *m = &cl->class_class_members[i];
2423 vim_free(m->ocm_name);
2424 vim_free(m->ocm_init);
2425 if (cl->class_members_tv != NULL)
2426 clear_tv(&cl->class_members_tv[i]);
2427 }
2428 vim_free(cl->class_class_members);
2429 vim_free(cl->class_members_tv);
2430
2431 for (int i = 0; i < cl->class_obj_member_count; ++i)
2432 {
2433 ocmember_T *m = &cl->class_obj_members[i];
2434 vim_free(m->ocm_name);
2435 vim_free(m->ocm_init);
2436 }
2437 vim_free(cl->class_obj_members);
2438
2439 for (int i = 0; i < cl->class_class_function_count; ++i)
2440 {
2441 ufunc_T *uf = cl->class_class_functions[i];
2442 func_clear_free(uf, FALSE);
2443 }
2444 vim_free(cl->class_class_functions);
2445
2446 for (int i = 0; i < cl->class_obj_method_count; ++i)
2447 {
2448 ufunc_T *uf = cl->class_obj_methods[i];
2449 func_clear_free(uf, FALSE);
2450 }
2451 vim_free(cl->class_obj_methods);
2452
2453 clear_type_list(&cl->class_type_list);
2454
2455 class_cleared(cl);
2456
2457 vim_free(cl);
2458}
2459
2460/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002461 * Unreference a class. Free it when the reference count goes down to zero.
2462 */
2463 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002464class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002465{
Bram Moolenaard505d172022-12-18 21:42:55 +00002466 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002467 class_free(cl);
2468}
2469
2470/*
2471 * Go through the list of all classes and free items without "copyID".
2472 */
2473 int
2474class_free_nonref(int copyID)
2475{
2476 int did_free = FALSE;
2477
2478 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002479 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002480 next_nonref_class = cl->class_next_used;
2481 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002482 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002483 // Free the class and items it contains.
2484 class_free(cl);
2485 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002486 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002487 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002488
2489 next_nonref_class = NULL;
2490 return did_free;
2491}
2492
2493 int
2494set_ref_in_classes(int copyID)
2495{
2496 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
2497 set_ref_in_item_class(cl, copyID, NULL, NULL);
2498
2499 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002500}
2501
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002502static object_T *first_object = NULL;
2503
2504/*
2505 * Call this function when an object has been created. It will be added to the
2506 * list headed by "first_object".
2507 */
2508 void
2509object_created(object_T *obj)
2510{
2511 if (first_object != NULL)
2512 {
2513 obj->obj_next_used = first_object;
2514 first_object->obj_prev_used = obj;
2515 }
2516 first_object = obj;
2517}
2518
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002519static object_T *next_nonref_obj = NULL;
2520
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002521/*
2522 * Call this function when an object has been cleared and is about to be freed.
2523 * It is removed from the list headed by "first_object".
2524 */
2525 void
2526object_cleared(object_T *obj)
2527{
2528 if (obj->obj_next_used != NULL)
2529 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
2530 if (obj->obj_prev_used != NULL)
2531 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
2532 else if (first_object == obj)
2533 first_object = obj->obj_next_used;
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002534
2535 // update the next object to check if needed
2536 if (obj == next_nonref_obj)
2537 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002538}
2539
2540/*
2541 * Go through the list of all objects and free items without "copyID".
2542 */
2543 int
2544object_free_nonref(int copyID)
2545{
2546 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002547
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002548 for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002549 {
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002550 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002551 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
2552 {
2553 // Free the object and items it contains.
2554 object_clear(obj);
2555 did_free = TRUE;
2556 }
2557 }
2558
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002559 next_nonref_obj = NULL;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002560 return did_free;
2561}
2562
LemonBoyafe04662023-08-23 21:08:11 +02002563/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002564 * Echo a class or object method not found message.
2565 */
2566 void
2567method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
2568{
2569 char_u *method_name = vim_strnsave(name, len);
2570 if ((v_type == VAR_OBJECT)
2571 && (class_method_idx(cl, name, len) >= 0))
2572 {
2573 // If this is a class method, then give a different error
2574 if (*name == '_')
2575 semsg(_(e_cannot_access_private_method_str), method_name);
2576 else
2577 semsg(_(e_class_member_str_accessible_only_using_class_str),
2578 method_name, cl->class_name);
2579 }
2580 else if ((v_type == VAR_CLASS)
2581 && (object_method_idx(cl, name, len) >= 0))
2582 {
2583 // If this is an object method, then give a different error
2584 if (*name == '_')
2585 semsg(_(e_cannot_access_private_method_str), method_name);
2586 else
2587 semsg(_(e_object_member_str_accessible_only_using_object_str),
2588 method_name, cl->class_name);
2589 }
2590 else
2591 semsg(_(e_method_not_found_on_class_str_str), cl->class_name,
2592 method_name);
2593 vim_free(method_name);
2594}
2595
2596/*
2597 * Echo a class or object member not found message.
2598 */
2599 void
2600member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
2601{
2602 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
2603
2604 if (v_type == VAR_OBJECT)
2605 {
2606 if (class_member_idx(cl, name, len) >= 0)
2607 semsg(_(e_class_member_str_accessible_only_using_class_str),
2608 varname, cl->class_name);
2609 else
2610 semsg(_(e_member_not_found_on_object_str_str), cl->class_name,
2611 varname);
2612 }
2613 else
2614 {
2615 if (object_member_idx(cl, name, len) >= 0)
2616 semsg(_(e_object_member_str_accessible_only_using_object_str),
2617 varname, cl->class_name);
2618 else
2619 semsg(_(e_class_member_str_not_found_in_class_str),
2620 varname, cl->class_name);
2621 }
2622 vim_free(varname);
2623}
2624
2625/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002626 * Return TRUE when the class "cl", its base class or one of the implemented
2627 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02002628 */
2629 int
2630class_instance_of(class_T *cl, class_T *other_cl)
2631{
2632 if (cl == other_cl)
2633 return TRUE;
2634
2635 // Recursively check the base classes.
2636 for (; cl != NULL; cl = cl->class_extends)
2637 {
2638 if (cl == other_cl)
2639 return TRUE;
2640 // Check the implemented interfaces.
2641 for (int i = cl->class_interface_count - 1; i >= 0; --i)
2642 if (cl->class_interfaces_cl[i] == other_cl)
2643 return TRUE;
2644 }
2645
2646 return FALSE;
2647}
2648
2649/*
2650 * "instanceof(object, classinfo)" function
2651 */
2652 void
2653f_instanceof(typval_T *argvars, typval_T *rettv)
2654{
2655 typval_T *object_tv = &argvars[0];
2656 typval_T *classinfo_tv = &argvars[1];
2657 listitem_T *li;
2658
2659 rettv->vval.v_number = VVAL_FALSE;
2660
2661 if (check_for_object_arg(argvars, 0) == FAIL
2662 || check_for_class_or_list_arg(argvars, 1) == FAIL)
2663 return;
2664
2665 if (classinfo_tv->v_type == VAR_LIST)
2666 {
2667 FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
2668 {
2669 if (li->li_tv.v_type != VAR_CLASS)
2670 {
2671 emsg(_(e_class_required));
2672 return;
2673 }
2674
2675 if (class_instance_of(object_tv->vval.v_object->obj_class,
2676 li->li_tv.vval.v_class) == TRUE)
2677 {
2678 rettv->vval.v_number = VVAL_TRUE;
2679 return;
2680 }
2681 }
2682 }
2683 else if (classinfo_tv->v_type == VAR_CLASS)
2684 {
2685 rettv->vval.v_number = class_instance_of(object_tv->vval.v_object->obj_class,
2686 classinfo_tv->vval.v_class);
2687 }
2688}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002689
2690#endif // FEAT_EVAL