blob: 2c5e1dffa349e71bfeba50c5b03ec6f319893334 [file] [log] [blame]
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * vim9class.c: Vim9 script class support
12 */
13
14#define USING_FLOAT_STUFF
15#include "vim.h"
16
17#if defined(FEAT_EVAL) || defined(PROTO)
18
19// When not generating protos this is included in proto.h
20#ifdef PROTO
21# include "vim9.h"
22#endif
23
Yegappan Lakshmanane651e112023-09-04 07:51:01 +020024static class_T *first_class = NULL;
25static class_T *next_nonref_class = NULL;
26
27/*
28 * Call this function when a class has been created. It will be added to the
29 * list headed by "first_class".
30 */
31 static void
32class_created(class_T *cl)
33{
34 if (first_class != NULL)
35 {
36 cl->class_next_used = first_class;
37 first_class->class_prev_used = cl;
38 }
39 first_class = cl;
40}
41
42/*
43 * Call this function when a class has been cleared and is about to be freed.
44 * It is removed from the list headed by "first_class".
45 */
46 static void
47class_cleared(class_T *cl)
48{
49 if (cl->class_next_used != NULL)
50 cl->class_next_used->class_prev_used = cl->class_prev_used;
51 if (cl->class_prev_used != NULL)
52 cl->class_prev_used->class_next_used = cl->class_next_used;
53 else if (first_class == cl)
54 first_class = cl->class_next_used;
55
56 // update the next class to check if needed
57 if (cl == next_nonref_class)
58 next_nonref_class = cl->class_next_used;
59}
60
Bram Moolenaarc1c365c2022-12-04 20:13:24 +000061/*
Bram Moolenaard505d172022-12-18 21:42:55 +000062 * Parse a member declaration, both object and class member.
63 * Returns OK or FAIL. When OK then "varname_end" is set to just after the
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +020064 * variable name and "type_ret" is set to the declared or detected type.
Bram Moolenaard505d172022-12-18 21:42:55 +000065 * "init_expr" is set to the initialisation expression (allocated), if there is
Bram Moolenaar554d0312023-01-05 19:59:18 +000066 * one. For an interface "init_expr" is NULL.
Bram Moolenaard505d172022-12-18 21:42:55 +000067 */
68 static int
69parse_member(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020070 exarg_T *eap,
71 char_u *line,
72 char_u *varname,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020073 int has_public, // TRUE if "public" seen before "varname"
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020074 char_u **varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +020075 int *has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020076 garray_T *type_list,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020077 type_T **type_ret,
78 char_u **init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +000079{
80 *varname_end = to_name_end(varname, FALSE);
81 if (*varname == '_' && has_public)
82 {
RestorerZ7fe8f432023-09-24 23:21:24 +020083 semsg(_(e_public_variable_name_cannot_start_with_underscore_str), line);
Bram Moolenaard505d172022-12-18 21:42:55 +000084 return FAIL;
85 }
86
87 char_u *colon = skipwhite(*varname_end);
88 char_u *type_arg = colon;
89 type_T *type = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +020090 *has_type = FALSE;
Bram Moolenaard505d172022-12-18 21:42:55 +000091 if (*colon == ':')
92 {
93 if (VIM_ISWHITE(**varname_end))
94 {
95 semsg(_(e_no_white_space_allowed_before_colon_str), varname);
96 return FAIL;
97 }
98 if (!VIM_ISWHITE(colon[1]))
99 {
100 semsg(_(e_white_space_required_after_str_str), ":", varname);
101 return FAIL;
102 }
103 type_arg = skipwhite(colon + 1);
104 type = parse_type(&type_arg, type_list, TRUE);
105 if (type == NULL)
106 return FAIL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200107 *has_type = TRUE;
Bram Moolenaard505d172022-12-18 21:42:55 +0000108 }
109
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200110 char_u *init_arg = skipwhite(type_arg);
111 if (type == NULL && *init_arg != '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000112 {
113 emsg(_(e_type_or_initialization_required));
114 return FAIL;
115 }
116
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200117 if (init_expr == NULL && *init_arg == '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000118 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200119 emsg(_(e_cannot_initialize_variable_in_interface));
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200120 return FAIL;
121 }
122
123 if (*init_arg == '=')
124 {
125 evalarg_T evalarg;
126 char_u *expr_start, *expr_end;
127
128 if (!VIM_ISWHITE(init_arg[-1]) || !VIM_ISWHITE(init_arg[1]))
Bram Moolenaard505d172022-12-18 21:42:55 +0000129 {
130 semsg(_(e_white_space_required_before_and_after_str_at_str),
131 "=", type_arg);
132 return FAIL;
133 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200134 init_arg = skipwhite(init_arg + 1);
Bram Moolenaard505d172022-12-18 21:42:55 +0000135
Bram Moolenaard505d172022-12-18 21:42:55 +0000136 fill_evalarg_from_eap(&evalarg, eap, FALSE);
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200137 (void)skip_expr_concatenate(&init_arg, &expr_start, &expr_end, &evalarg);
Bram Moolenaard505d172022-12-18 21:42:55 +0000138
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +0200139 // No type specified for the member. Set it to "any" and the correct
140 // type will be set when the object is instantiated.
Bram Moolenaard505d172022-12-18 21:42:55 +0000141 if (type == NULL)
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200142 type = &t_any;
Bram Moolenaard505d172022-12-18 21:42:55 +0000143
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200144 *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
145 // Free the memory pointed by expr_start.
Bram Moolenaard505d172022-12-18 21:42:55 +0000146 clear_evalarg(&evalarg, NULL);
147 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200148 else if (!valid_declaration_type(type))
Bram Moolenaard505d172022-12-18 21:42:55 +0000149 return FAIL;
150
151 *type_ret = type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000152 return OK;
153}
154
155/*
156 * Add a member to an object or a class.
157 * Returns OK when successful, "init_expr" will be consumed then.
158 * Returns FAIL otherwise, caller might need to free "init_expr".
159 */
160 static int
161add_member(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200162 garray_T *gap,
163 char_u *varname,
164 char_u *varname_end,
165 int has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200166 int has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200167 type_T *type,
168 char_u *init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +0000169{
170 if (ga_grow(gap, 1) == FAIL)
171 return FAIL;
172 ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
173 m->ocm_name = vim_strnsave(varname, varname_end - varname);
=?UTF-8?q?Ola=20S=C3=B6der?=d8742472023-03-05 13:12:32 +0000174 m->ocm_access = has_public ? VIM_ACCESS_ALL
175 : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200176 m->ocm_has_type = has_type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000177 m->ocm_type = type;
178 if (init_expr != NULL)
179 m->ocm_init = init_expr;
180 ++gap->ga_len;
181 return OK;
182}
183
184/*
185 * Move the class or object members found while parsing a class into the class.
186 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000187 * "parent_members" points to the members in the parent class (if any)
188 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000189 * "members" will be set to the newly allocated array of members and
190 * "member_count" set to the number of members.
191 * Returns OK or FAIL.
192 */
193 static int
194add_members_to_class(
195 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000196 ocmember_T *parent_members,
197 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000198 ocmember_T **members,
199 int *member_count)
200{
Bram Moolenaar83677162023-01-08 19:54:10 +0000201 *member_count = parent_count + gap->ga_len;
202 *members = *member_count == 0 ? NULL
203 : ALLOC_MULT(ocmember_T, *member_count);
204 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000205 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000206 for (int i = 0; i < parent_count; ++i)
207 {
208 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000209 ocmember_T *m = *members + i;
210 *m = parent_members[i];
211 m->ocm_name = vim_strsave(m->ocm_name);
212 if (m->ocm_init != NULL)
213 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000214 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000215 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000216 // new members are moved
217 mch_memmove(*members + parent_count,
218 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000219 VIM_CLEAR(gap->ga_data);
220 return OK;
221}
222
223/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000224 * Convert a member index "idx" of interface "itf" to the member index of class
225 * "cl" implementing that interface.
226 */
227 int
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200228object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000229{
Ernie Rael18143d32023-09-04 22:30:41 +0200230 if (idx >= (is_method ? itf->class_obj_method_count
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200231 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000232 {
233 siemsg("index %d out of range for interface %s", idx, itf->class_name);
234 return 0;
235 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200236
237 // If "cl" is the interface or the class that is extended, then the method
238 // index can be used directly and there is no need to search for the method
239 // index in one of the child classes.
240 if (cl == itf)
241 return idx;
242
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200243 itf2class_T *i2c = NULL;
244 int searching = TRUE;
245 int method_offset = 0;
246
Ernie Raelcf138d42023-09-06 20:45:03 +0200247 for (class_T *super = cl; super != NULL && searching;
248 super = super->class_extends)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200249 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200250 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200251 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200252 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
253 {
254 searching = FALSE;
255 break;
256 }
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200257 }
258 if (searching && is_method)
259 // The parent class methods are stored after the current class
260 // methods.
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200261 method_offset += super->class_obj_method_count_child;
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200262 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000263 if (i2c == NULL)
264 {
265 siemsg("class %s not found on interface %s",
266 cl->class_name, itf->class_name);
267 return 0;
268 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +0200269
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200270 // A table follows the i2c for the class
271 int *table = (int *)(i2c + 1);
272 // "method_offset" is 0, if method is in the current class. If method
273 // is in a parent class, then it is non-zero.
274 return table[idx] + method_offset;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000275}
276
277/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200278 * Check whether a class named "extends_name" is present. If the class is
279 * valid, then "extends_clp" is set with the class pointer.
280 * Returns TRUE if the class name "extends_names" is a valid class.
281 */
282 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200283validate_extends_class(
284 char_u *extends_name,
285 class_T **extends_clp,
286 int is_class)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200287{
288 typval_T tv;
289 int success = FALSE;
290
291 tv.v_type = VAR_UNKNOWN;
292 if (eval_variable_import(extends_name, &tv) == FAIL)
293 {
294 semsg(_(e_class_name_not_found_str), extends_name);
295 return success;
296 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200297
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200298 if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
299 || (is_class
300 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
301 || (!is_class
302 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0))
303 // a interface cannot extend a class and a class cannot extend an
304 // interface.
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200305 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200306 else
307 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200308 class_T *extends_cl = tv.vval.v_class;
309 ++extends_cl->class_refcount;
310 *extends_clp = extends_cl;
311 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200312 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200313 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200314
315 return success;
316}
317
318/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200319 * Check method names in the parent class lineage to make sure the access is
320 * the same for overridden methods.
321 */
322 static int
323validate_extends_methods(
324 garray_T *objmethods_gap,
325 class_T *extends_cl)
326{
327 class_T *super = extends_cl;
328 int method_count = objmethods_gap->ga_len;
329 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
330
331 while (super != NULL)
332 {
333 int extends_method_count = super->class_obj_method_count_child;
334 if (extends_method_count == 0)
335 {
336 super = super->class_extends;
337 continue;
338 }
339
340 ufunc_T **extends_methods = super->class_obj_methods;
341
342 for (int i = 0; i < extends_method_count; i++)
343 {
344 char_u *pstr = extends_methods[i]->uf_name;
345 int extends_private = (*pstr == '_');
346 if (extends_private)
347 pstr++;
348
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200349 // When comparing the method names, ignore the access type (public
350 // and private methods are considered the same).
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200351 for (int j = 0; j < method_count; j++)
352 {
353 char_u *qstr = cl_fp[j]->uf_name;
354 int priv_method = (*qstr == '_');
355 if (priv_method)
356 qstr++;
357 if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
358 {
359 // Method access is different between the super class and
360 // the subclass
361 semsg(_(e_method_str_of_class_str_has_different_access),
362 cl_fp[j]->uf_name, super->class_name);
363 return FALSE;
364 }
365 }
366 }
367 super = super->class_extends;
368 }
369
370 return TRUE;
371}
372
373/*
374 * Check whether a object member variable in "objmembers_gap" is a duplicate of
375 * a member in any of the extended parent class lineage. Returns TRUE if there
376 * are no duplicates.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200377 */
378 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200379extends_check_dup_members(
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200380 garray_T *objmembers_gap,
381 class_T *extends_cl)
382{
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200383 int member_count = objmembers_gap->ga_len;
384 if (member_count == 0)
385 return TRUE;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200386
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200387 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
388
389 // Validate each member variable
390 for (int c_i = 0; c_i < member_count; c_i++)
391 {
392 class_T *p_cl = extends_cl;
393 ocmember_T *c_m = members + c_i;
394 char_u *pstr = (*c_m->ocm_name == '_')
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200395 ? c_m->ocm_name + 1 : c_m->ocm_name;
396
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200397 // Check in all the parent classes in the lineage
398 while (p_cl != NULL)
399 {
400 int p_member_count = p_cl->class_obj_member_count;
401 if (p_member_count == 0)
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200402 {
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200403 p_cl = p_cl->class_extends;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200404 continue;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200405 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200406 ocmember_T *p_members = p_cl->class_obj_members;
407
408 // Compare against all the members in the parent class
409 for (int p_i = 0; p_i < p_member_count; p_i++)
410 {
411 ocmember_T *p_m = p_members + p_i;
412 char_u *qstr = (*p_m->ocm_name == '_')
413 ? p_m->ocm_name + 1 : p_m->ocm_name;
414 if (STRCMP(pstr, qstr) == 0)
415 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200416 semsg(_(e_duplicate_variable_str), c_m->ocm_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200417 return FALSE;
418 }
419 }
420
421 p_cl = p_cl->class_extends;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200422 }
423 }
424
425 return TRUE;
426}
427
428/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200429 * Compare the variable type of interface variables in "objmembers_gap" against
430 * the variable in any of the extended super interface lineage. Used to
431 * compare the variable types when extending interfaces. Returns TRUE if the
432 * variable types are the same.
433 */
434 static int
435extends_check_intf_var_type(
436 garray_T *objmembers_gap,
437 class_T *extends_cl)
438{
439 int member_count = objmembers_gap->ga_len;
440 if (member_count == 0)
441 return TRUE;
442
443 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
444
445 // Validate each member variable
446 for (int c_i = 0; c_i < member_count; c_i++)
447 {
448 class_T *p_cl = extends_cl;
449 ocmember_T *c_m = members + c_i;
450 int var_found = FALSE;
451
452 // Check in all the parent classes in the lineage
453 while (p_cl != NULL && !var_found)
454 {
455 int p_member_count = p_cl->class_obj_member_count;
456 if (p_member_count == 0)
457 {
458 p_cl = p_cl->class_extends;
459 continue;
460 }
461 ocmember_T *p_members = p_cl->class_obj_members;
462
463 // Compare against all the members in the parent class
464 for (int p_i = 0; p_i < p_member_count; p_i++)
465 {
466 where_T where = WHERE_INIT;
467 ocmember_T *p_m = p_members + p_i;
468
469 if (STRCMP(p_m->ocm_name, c_m->ocm_name) != 0)
470 continue;
471
472 // Ensure the type is matching.
473 where.wt_func_name = (char *)c_m->ocm_name;
474 where.wt_kind = WT_MEMBER;
475
476 if (check_type(p_m->ocm_type, c_m->ocm_type, TRUE,
477 where) == FAIL)
478 return FALSE;
479
480 var_found = TRUE;
481 }
482
483 p_cl = p_cl->class_extends;
484 }
485 }
486
487 return TRUE;
488}
489
490/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200491 * When extending an abstract class, check whether all the abstract methods in
492 * the parent class are implemented. Returns TRUE if all the methods are
493 * implemented.
494 */
495 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200496validate_abstract_class_methods(
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200497 garray_T *classmethods_gap,
498 garray_T *objmethods_gap,
499 class_T *extends_cl)
500{
501 for (int loop = 1; loop <= 2; ++loop)
502 {
503 // loop == 1: check class methods
504 // loop == 2: check object methods
505 int extends_method_count = loop == 1
506 ? extends_cl->class_class_function_count
507 : extends_cl->class_obj_method_count;
508 if (extends_method_count == 0)
509 continue;
510
511 ufunc_T **extends_methods = loop == 1
512 ? extends_cl->class_class_functions
513 : extends_cl->class_obj_methods;
514
515 int method_count = loop == 1 ? classmethods_gap->ga_len
516 : objmethods_gap->ga_len;
517 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
518 ? classmethods_gap->ga_data
519 : objmethods_gap->ga_data);
520
521 for (int i = 0; i < extends_method_count; i++)
522 {
523 ufunc_T *uf = extends_methods[i];
Yegappan Lakshmanan1db15142023-09-19 20:34:05 +0200524 if (!IS_ABSTRACT_METHOD(uf))
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200525 continue;
526
527 int method_found = FALSE;
528
529 for (int j = 0; j < method_count; j++)
530 {
531 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
532 {
533 method_found = TRUE;
534 break;
535 }
536 }
537
538 if (!method_found)
539 {
540 semsg(_(e_abstract_method_str_not_found), uf->uf_name);
541 return FALSE;
542 }
543 }
544 }
545
546 return TRUE;
547}
548
549/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200550 * Returns TRUE if the interface variable "if_var" is present in the list of
551 * variables in "cl_mt" or in the parent lineage of one of the extended classes
552 * in "extends_cl". For a class variable, 'is_class_var' is TRUE.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200553 */
554 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200555intf_variable_present(
556 char_u *intf_class_name,
557 ocmember_T *if_var,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200558 ocmember_T *cl_mt,
559 int cl_member_count,
560 class_T *extends_cl)
561{
562 int variable_present = FALSE;
563
564 for (int cl_i = 0; cl_i < cl_member_count; ++cl_i)
565 {
566 ocmember_T *m = &cl_mt[cl_i];
567 where_T where = WHERE_INIT;
568
569 if (STRCMP(if_var->ocm_name, m->ocm_name) != 0)
570 continue;
571
572 // Ensure the access type is same
573 if (if_var->ocm_access != m->ocm_access)
574 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200575 semsg(_(e_variable_str_of_interface_str_has_different_access),
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200576 if_var->ocm_name, intf_class_name);
577 return FALSE;
578 }
579
580 // Ensure the type is matching.
581 if (m->ocm_type == &t_any)
582 {
583 // variable type is not specified. Use the variable type in the
584 // interface.
585 m->ocm_type = if_var->ocm_type;
586 }
587 else
588 {
589 where.wt_func_name = (char *)m->ocm_name;
590 where.wt_kind = WT_MEMBER;
591 if (check_type(if_var->ocm_type, m->ocm_type, TRUE,
592 where) == FAIL)
593 return FALSE;
594 }
595
596 variable_present = TRUE;
597 break;
598 }
599
600 if (!variable_present && extends_cl != NULL)
601 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200602 int ext_cl_count = extends_cl->class_obj_member_count;
603 ocmember_T *ext_cl_mt = extends_cl->class_obj_members;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200604 return intf_variable_present(intf_class_name, if_var,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200605 ext_cl_mt, ext_cl_count,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200606 extends_cl->class_extends);
607 }
608
609 return variable_present;
610}
611
612/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200613 * Check the variables of the interface class "ifcl" match object variables
614 * ("objmembers_gap") of a class.
615 * Returns TRUE if the object variables names are valid.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200616 */
617 static int
618validate_interface_variables(
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200619 char_u *intf_class_name,
620 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200621 garray_T *objmembers_gap,
622 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200623{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200624 int if_count = ifcl->class_obj_member_count;
625 if (if_count == 0)
626 return TRUE;
627
628 ocmember_T *if_ms = ifcl->class_obj_members;
629 ocmember_T *cl_ms = (ocmember_T *)(objmembers_gap->ga_data);
630 int cl_count = objmembers_gap->ga_len;
631 for (int if_i = 0; if_i < if_count; ++if_i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200632 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200633 if (!intf_variable_present(intf_class_name, &if_ms[if_i], cl_ms,
634 cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200635 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200636 semsg(_(e_variable_str_of_interface_str_not_implemented),
637 if_ms[if_i].ocm_name, intf_class_name);
638 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200639 }
640 }
641
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200642 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200643}
644
645/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200646 * Returns TRUE if the method signature of "if_method" and "cl_method" matches.
647 */
648 static int
649intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method)
650{
651 where_T where = WHERE_INIT;
652
653 // Ensure the type is matching.
654 where.wt_func_name = (char *)if_method->uf_name;
655 where.wt_kind = WT_METHOD;
656 if (check_type(if_method->uf_func_type, cl_method->uf_func_type, TRUE,
657 where) == FAIL)
658 return FALSE;
659
660 return TRUE;
661}
662
663/*
664 * Returns TRUE if the interface method "if_ufunc" is present in the list of
665 * methods in "cl_fp" or in the parent lineage of one of the extended classes
666 * in "extends_cl". For a class method, 'is_class_method' is TRUE.
667 */
668 static int
669intf_method_present(
670 ufunc_T *if_ufunc,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200671 ufunc_T **cl_fp,
672 int cl_count,
673 class_T *extends_cl)
674{
675 int method_present = FALSE;
676
677 for (int cl_i = 0; cl_i < cl_count; ++cl_i)
678 {
679 char_u *cl_name = cl_fp[cl_i]->uf_name;
680 if (STRCMP(if_ufunc->uf_name, cl_name) == 0)
681 {
682 // Ensure the type is matching.
683 if (!intf_method_type_matches(if_ufunc, cl_fp[cl_i]))
684 return FALSE;
685 method_present = TRUE;
686 break;
687 }
688 }
689
690 if (!method_present && extends_cl != NULL)
691 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200692 ufunc_T **ext_cl_fp = (ufunc_T **)(extends_cl->class_obj_methods);
693 int ext_cl_count = extends_cl->class_obj_method_count;
694 return intf_method_present(if_ufunc, ext_cl_fp, ext_cl_count,
695 extends_cl->class_extends);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200696 }
697
698 return method_present;
699}
700
701/*
702 * Validate that a new class implements all the class/instance methods in the
703 * interface "ifcl". The new class methods are in "classfunctions_gap" and the
704 * new object methods are in "objmemthods_gap". Also validates the method
705 * types.
706 * Returns TRUE if all the interface class/object methods are implemented in
707 * the new class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200708 */
709 static int
710validate_interface_methods(
711 char_u *intf_class_name,
712 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200713 garray_T *objmethods_gap,
714 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200715{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200716 int if_count = ifcl->class_obj_method_count;
717 if (if_count == 0)
718 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200719
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200720 ufunc_T **if_fp = ifcl->class_obj_methods;
721 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
722 int cl_count = objmethods_gap->ga_len;
723 for (int if_i = 0; if_i < if_count; ++if_i)
724 {
725 char_u *if_name = if_fp[if_i]->uf_name;
726
727 if (!intf_method_present(if_fp[if_i], cl_fp, cl_count, extends_cl))
728 {
729 semsg(_(e_method_str_of_interface_str_not_implemented),
730 if_name, intf_class_name);
731 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200732 }
733 }
734
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200735 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200736}
737
738/*
739 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200740 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200741 * object methods and object members in the new class are in
742 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
743 * "objmembers_gap" respectively.
744 */
745 static int
746validate_implements_classes(
747 garray_T *impl_gap,
748 class_T **intf_classes,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200749 garray_T *objmethods_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200750 garray_T *objmembers_gap,
751 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200752{
753 int success = TRUE;
754
755 for (int i = 0; i < impl_gap->ga_len && success; ++i)
756 {
757 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
758 typval_T tv;
759 tv.v_type = VAR_UNKNOWN;
760 if (eval_variable_import(impl, &tv) == FAIL)
761 {
762 semsg(_(e_interface_name_not_found_str), impl);
763 success = FALSE;
764 break;
765 }
766
767 if (tv.v_type != VAR_CLASS
768 || tv.vval.v_class == NULL
769 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
770 {
771 semsg(_(e_not_valid_interface_str), impl);
772 success = FALSE;
773 clear_tv(&tv);
774 break;
775 }
776
777 class_T *ifcl = tv.vval.v_class;
778 intf_classes[i] = ifcl;
779 ++ifcl->class_refcount;
780
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200781 // check the variables of the interface match the members of the class
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200782 success = validate_interface_variables(impl, ifcl, objmembers_gap,
783 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200784
785 // check the functions/methods of the interface match the
786 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200787 if (success)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200788 success = validate_interface_methods(impl, ifcl, objmethods_gap,
789 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200790 clear_tv(&tv);
791 }
792
793 return success;
794}
795
796/*
797 * Check no function argument name is used as a class member.
798 * (Object members are always accessed with "this." prefix, so no need
799 * to check them.)
800 */
801 static int
802check_func_arg_names(
803 garray_T *classfunctions_gap,
804 garray_T *objmethods_gap,
805 garray_T *classmembers_gap)
806{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200807 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200808 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200809 {
810 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
811
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200812 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200813 {
814 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
815
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200816 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200817 {
818 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
819 garray_T *mgap = classmembers_gap;
820
821 // Check all the class member names
822 for (int mi = 0; mi < mgap->ga_len; ++mi)
823 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200824 char_u *mname =
825 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200826 if (STRCMP(aname, mname) == 0)
827 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200828 if (uf->uf_script_ctx.sc_sid > 0)
829 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
830
831 semsg(_(e_argument_already_declared_in_class_str),
832 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200833
834 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200835 }
836 }
837 }
838 }
839 }
840
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200841 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200842}
843
844/*
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200845 * Returns TRUE if 'varname' is a reserved keyword name
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200846 */
847 static int
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200848is_reserved_varname(char_u *varname, char_u *varname_end)
849{
850 int reserved = FALSE;
851 char_u save_varname_end = *varname_end;
852 *varname_end = NUL;
853
854 reserved = check_reserved_name(varname, FALSE) == FAIL;
855
856 *varname_end = save_varname_end;
857
858 return reserved;
859}
860
861/*
862 * Returns TRUE if the variable "varname" is already defined either as a class
863 * variable or as an object variable.
864 */
865 static int
866is_duplicate_variable(
867 garray_T *class_members,
868 garray_T *obj_members,
869 char_u *varname,
870 char_u *varname_end)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200871{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200872 char_u *name = vim_strnsave(varname, varname_end - varname);
873 char_u *pstr = (*name == '_') ? name + 1 : name;
874 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200875
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200876 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200877 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200878 // loop == 1: class variables, loop == 2: object variables
879 garray_T *vgap = (loop == 1) ? class_members : obj_members;
880 for (int i = 0; i < vgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200881 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200882 ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i;
883 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1
884 : m->ocm_name;
885 if (STRCMP(pstr, qstr) == 0)
886 {
887 semsg(_(e_duplicate_variable_str), name);
888 dup = TRUE;
889 break;
890 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200891 }
892 }
893
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200894 vim_free(name);
895 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200896}
897
898/*
899 * Returns TRUE if the method "name" is already defined.
900 */
901 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200902is_duplicate_method(
903 garray_T *classmethods_gap,
904 garray_T *objmethods_gap,
905 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200906{
907 char_u *pstr = (*name == '_') ? name + 1 : name;
908
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200909 // loop 1: class methods, loop 2: object methods
910 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200911 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200912 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
913 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200914 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200915 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
916 char_u *qstr = *n == '_' ? n + 1 : n;
917 if (STRCMP(pstr, qstr) == 0)
918 {
919 semsg(_(e_duplicate_function_str), name);
920 return TRUE;
921 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200922 }
923 }
924
925 return FALSE;
926}
927
928/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200929 * Returns TRUE if the constructor is valid.
930 */
931 static int
932is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
933{
934 // Constructors are not allowed in abstract classes.
935 if (is_abstract)
936 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200937 emsg(_(e_cannot_define_new_method_in_abstract_class));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200938 return FALSE;
939 }
940 // A constructor is always static, no need to define it so.
941 if (has_static)
942 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200943 emsg(_(e_cannot_define_new_method_as_static));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200944 return FALSE;
945 }
946 // A return type should not be specified for the new()
947 // constructor method.
948 if (uf->uf_ret_type->tt_type != VAR_VOID)
949 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200950 emsg(_(e_cannot_use_a_return_type_with_new_method));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200951 return FALSE;
952 }
953 return TRUE;
954}
955
956/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200957 * Update the interface class lookup table for the member index on the
958 * interface to the member index in the class implementing the interface.
959 * And a lookup table for the object method index on the interface
960 * to the object method index in the class implementing the interface.
961 * This is also used for updating the lookup table for the extended class
962 * hierarchy.
963 */
964 static int
965update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200966 class_T *ifcl,
967 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200968 garray_T *objmethods,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200969 int pobj_method_offset)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200970{
971 if (ifcl == NULL)
972 return OK;
973
974 // Table for members.
975 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200976 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200977 if (if2cl == NULL)
978 return FAIL;
979 if2cl->i2c_next = ifcl->class_itf2class;
980 ifcl->class_itf2class = if2cl;
981 if2cl->i2c_class = cl;
982 if2cl->i2c_is_method = FALSE;
983
984 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
985 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
986 {
987 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200988 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200989 {
990 int *table = (int *)(if2cl + 1);
991 table[if_i] = cl_i;
992 break;
993 }
994 }
995
996 // Table for methods.
997 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200998 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200999 if (if2cl == NULL)
1000 return FAIL;
1001 if2cl->i2c_next = ifcl->class_itf2class;
1002 ifcl->class_itf2class = if2cl;
1003 if2cl->i2c_class = cl;
1004 if2cl->i2c_is_method = TRUE;
1005
1006 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
1007 {
1008 int done = FALSE;
1009 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
1010 {
1011 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001012 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001013 {
1014 int *table = (int *)(if2cl + 1);
1015 table[if_i] = cl_i;
1016 done = TRUE;
1017 break;
1018 }
1019 }
1020
1021 // extended class object method is not overridden by the child class.
1022 // Keep the method declared in one of the parent classes in the
1023 // lineage.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001024 if (!done)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001025 {
1026 // If "ifcl" is not the immediate parent of "cl", then search in
1027 // the intermediate parent classes.
1028 if (cl->class_extends != ifcl)
1029 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001030 class_T *parent = cl->class_extends;
1031 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001032
1033 while (!done && parent != NULL && parent != ifcl)
1034 {
1035
1036 for (int cl_i = 0;
1037 cl_i < parent->class_obj_method_count_child; ++cl_i)
1038 {
1039 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
1040 parent->class_obj_methods[cl_i]->uf_name)
1041 == 0)
1042 {
1043 int *table = (int *)(if2cl + 1);
1044 table[if_i] = method_offset + cl_i;
1045 done = TRUE;
1046 break;
1047 }
1048 }
1049 method_offset += parent->class_obj_method_count_child;
1050 parent = parent->class_extends;
1051 }
1052 }
1053
1054 if (!done)
1055 {
1056 int *table = (int *)(if2cl + 1);
1057 table[if_i] = pobj_method_offset + if_i;
1058 }
1059 }
1060 }
1061
1062 return OK;
1063}
1064
1065/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001066 * Update the member and object method lookup tables for a new class in the
1067 * interface class.
1068 * For each interface add a lookup table for the member index on the interface
1069 * to the member index in the new class. And a lookup table for the object
1070 * method index on the interface to the object method index in the new class.
1071 */
1072 static int
1073add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
1074{
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001075 // update the lookup table for all the implemented interfaces
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001076 for (int i = 0; i < cl->class_interface_count; ++i)
1077 {
1078 class_T *ifcl = cl->class_interfaces_cl[i];
1079
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001080 // update the lookup table for this interface and all its super
1081 // interfaces.
1082 while (ifcl != NULL)
1083 {
1084 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
1085 0) == FAIL)
1086 return FAIL;
1087 ifcl = ifcl->class_extends;
1088 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001089 }
1090
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001091 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001092 if (extends_cl != NULL)
1093 {
1094 class_T *pclass = extends_cl;
1095 int pobj_method_offset = objmethods_gap->ga_len;
1096
1097 // Update the entire lineage of extended classes.
1098 while (pclass != NULL)
1099 {
1100 if (update_member_method_lookup_table(pclass, cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001101 objmethods_gap, pobj_method_offset) == FAIL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001102 return FAIL;
1103
1104 pobj_method_offset += pclass->class_obj_method_count_child;
1105 pclass = pclass->class_extends;
1106 }
1107 }
1108
1109 return OK;
1110}
1111
1112/*
1113 * Add class members to a new class. Allocate a typval for each class member
1114 * and initialize it.
1115 */
1116 static void
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001117add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001118{
1119 // Allocate a typval for each class member and initialize it.
1120 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
1121 cl->class_class_member_count);
1122 if (cl->class_members_tv == NULL)
1123 return;
1124
1125 for (int i = 0; i < cl->class_class_member_count; ++i)
1126 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001127 ocmember_T *m = &cl->class_class_members[i];
1128 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001129 if (m->ocm_init != NULL)
1130 {
1131 typval_T *etv = eval_expr(m->ocm_init, eap);
1132 if (etv != NULL)
1133 {
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001134 if (m->ocm_type->tt_type == VAR_ANY
1135 && !m->ocm_has_type
1136 && etv->v_type != VAR_SPECIAL)
1137 // If the member variable type is not yet set, then use
1138 // the initialization expression type.
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001139 m->ocm_type = typval2type(etv, get_copyID(),
1140 type_list_gap,
1141 TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001142 *tv = *etv;
1143 vim_free(etv);
1144 }
1145 }
1146 else
1147 {
1148 // TODO: proper default value
1149 tv->v_type = m->ocm_type->tt_type;
1150 tv->vval.v_string = NULL;
1151 }
1152 }
1153}
1154
1155/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001156 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001157 */
1158 static void
1159add_default_constructor(
1160 class_T *cl,
1161 garray_T *classfunctions_gap,
1162 garray_T *type_list_gap)
1163{
1164 garray_T fga;
1165
1166 ga_init2(&fga, 1, 1000);
1167 ga_concat(&fga, (char_u *)"new(");
1168 for (int i = 0; i < cl->class_obj_member_count; ++i)
1169 {
1170 if (i > 0)
1171 ga_concat(&fga, (char_u *)", ");
1172 ga_concat(&fga, (char_u *)"this.");
1173 ocmember_T *m = cl->class_obj_members + i;
1174 ga_concat(&fga, (char_u *)m->ocm_name);
1175 ga_concat(&fga, (char_u *)" = v:none");
1176 }
1177 ga_concat(&fga, (char_u *)")\nenddef\n");
1178 ga_append(&fga, NUL);
1179
1180 exarg_T fea;
1181 CLEAR_FIELD(fea);
1182 fea.cmdidx = CMD_def;
1183 fea.cmd = fea.arg = fga.ga_data;
1184
1185 garray_T lines_to_free;
1186 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1187
h-eastb895b0f2023-09-24 15:46:31 +02001188 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
1189 cl->class_obj_members, cl->class_obj_member_count);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001190
1191 ga_clear_strings(&lines_to_free);
1192 vim_free(fga.ga_data);
1193
1194 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1195 {
1196 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001197 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001198 ++classfunctions_gap->ga_len;
1199
1200 nf->uf_flags |= FC_NEW;
1201 nf->uf_ret_type = get_type_ptr(type_list_gap);
1202 if (nf->uf_ret_type != NULL)
1203 {
1204 nf->uf_ret_type->tt_type = VAR_OBJECT;
1205 nf->uf_ret_type->tt_class = cl;
1206 nf->uf_ret_type->tt_argcount = 0;
1207 nf->uf_ret_type->tt_args = NULL;
1208 }
1209 }
1210}
1211
1212/*
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001213 * Add the class methods and object methods to the new class "cl".
1214 * When extending a class "extends_cl", add the instance methods from the
1215 * parent class also.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001216 */
1217 static int
1218add_classfuncs_objmethods(
1219 class_T *cl,
1220 class_T *extends_cl,
1221 garray_T *classfunctions_gap,
1222 garray_T *objmethods_gap)
1223{
1224 // loop 1: class functions, loop 2: object methods
1225 for (int loop = 1; loop <= 2; ++loop)
1226 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001227 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1228 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001229 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001230 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001231 : &cl->class_obj_methods;
1232
1233 int parent_count = 0;
1234 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001235 // Include object methods from the parent.
1236 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001237 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001238 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001239 : extends_cl->class_obj_method_count;
1240
1241 *fcount = parent_count + gap->ga_len;
1242 if (*fcount == 0)
1243 {
1244 *fup = NULL;
1245 continue;
1246 }
1247 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1248 if (*fup == NULL)
1249 return FAIL;
1250
1251 if (gap->ga_len != 0)
1252 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
1253 vim_free(gap->ga_data);
1254 if (loop == 1)
1255 cl->class_class_function_count_child = gap->ga_len;
1256 else
1257 cl->class_obj_method_count_child = gap->ga_len;
1258
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001259 if (loop == 2)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001260 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001261 // Copy instance methods from the parent.
1262
1263 for (int i = 0; i < parent_count; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001264 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001265 // Can't use the same parent function, because "uf_class" is
1266 // different and compilation will have a different result.
1267 // Put them after the functions in the current class, object
1268 // methods may be overruled, then "super.Method()" is used to
1269 // find a method from the parent.
1270 ufunc_T *pf = (extends_cl->class_obj_methods)[i];
1271 (*fup)[gap->ga_len + i] = copy_function(pf);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001272
1273 // If the child class overrides a function from the parent
1274 // the signature must be equal.
1275 char_u *pname = pf->uf_name;
1276 for (int ci = 0; ci < gap->ga_len; ++ci)
1277 {
1278 ufunc_T *cf = (*fup)[ci];
1279 char_u *cname = cf->uf_name;
1280 if (STRCMP(pname, cname) == 0)
1281 {
1282 where_T where = WHERE_INIT;
1283 where.wt_func_name = (char *)pname;
1284 where.wt_kind = WT_METHOD;
1285 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1286 TRUE, where);
1287 }
1288 }
1289 }
1290 }
1291
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001292 // Set the class pointer on all the functions and object methods.
1293 for (int i = 0; i < *fcount; ++i)
1294 {
1295 ufunc_T *fp = (*fup)[i];
1296 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001297 if (i < gap->ga_len)
1298 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001299 if (loop == 2)
1300 fp->uf_flags |= FC_OBJECT;
1301 }
1302 }
1303
1304 return OK;
1305}
1306
1307/*
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001308 * Return the end of the class name starting at "arg". Valid characters in a
1309 * class name are alphanumeric characters and "_". Also handles imported class
1310 * names.
1311 */
1312 static char_u *
1313find_class_name_end(char_u *arg)
1314{
1315 char_u *end = arg;
1316
1317 while (ASCII_ISALNUM(*end) || *end == '_'
1318 || (*end == '.' && (ASCII_ISALNUM(end[1]) || end[1] == '_')))
1319 ++end;
1320
1321 return end;
1322}
1323
1324
1325/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001326 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001327 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001328 */
1329 void
1330ex_class(exarg_T *eap)
1331{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001332 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1333 long start_lnum = SOURCING_LNUM;
1334 char_u *arg = eap->arg;
1335 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001336
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001337 if (is_abstract)
1338 {
1339 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1340 {
1341 semsg(_(e_invalid_argument_str), arg);
1342 return;
1343 }
1344 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001345 is_class = TRUE;
1346 }
1347
1348 if (!current_script_is_vim9()
1349 || (cmdmod.cmod_flags & CMOD_LEGACY)
1350 || !getline_equal(eap->getline, eap->cookie, getsourceline))
1351 {
1352 if (is_class)
1353 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1354 else
1355 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1356 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001357 }
1358
1359 if (!ASCII_ISUPPER(*arg))
1360 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001361 if (is_class)
1362 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1363 else
1364 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1365 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001366 return;
1367 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001368 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1369 if (!IS_WHITE_OR_NUL(*name_end))
1370 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001371 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001372 return;
1373 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001374 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001375
Bram Moolenaara86655a2023-01-12 17:06:27 +00001376 // "export class" gets used when creating the class, don't use "is_export"
1377 // for the items inside the class.
1378 int class_export = is_export;
1379 is_export = FALSE;
1380
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001381 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001382 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001383
Bram Moolenaar83677162023-01-08 19:54:10 +00001384 // Name for "extends BaseClass"
1385 char_u *extends = NULL;
1386
Bram Moolenaar94674f22023-01-06 18:42:20 +00001387 // Names for "implements SomeInterface"
1388 garray_T ga_impl;
1389 ga_init2(&ga_impl, sizeof(char_u *), 5);
1390
1391 arg = skipwhite(name_end);
1392 while (*arg != NUL && *arg != '#' && *arg != '\n')
1393 {
1394 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001395 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001396 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1397 {
1398 if (extends != NULL)
1399 {
1400 emsg(_(e_duplicate_extends));
1401 goto early_ret;
1402 }
1403 arg = skipwhite(arg + 7);
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001404
1405 char_u *end = find_class_name_end(arg);
Bram Moolenaar83677162023-01-08 19:54:10 +00001406 if (!IS_WHITE_OR_NUL(*end))
1407 {
1408 semsg(_(e_white_space_required_after_name_str), arg);
1409 goto early_ret;
1410 }
1411 extends = vim_strnsave(arg, end - arg);
1412 if (extends == NULL)
1413 goto early_ret;
1414
1415 arg = skipwhite(end + 1);
1416 }
1417 else if (STRNCMP(arg, "implements", 10) == 0
1418 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001419 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001420 if (!is_class)
1421 {
1422 emsg(_(e_interface_cannot_use_implements));
1423 goto early_ret;
1424 }
1425
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001426 if (ga_impl.ga_len > 0)
1427 {
1428 emsg(_(e_duplicate_implements));
1429 goto early_ret;
1430 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001431 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001432
1433 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001434 {
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001435 char_u *impl_end = find_class_name_end(arg);
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001436 if ((!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1437 || (*impl_end == ','
1438 && !IS_WHITE_OR_NUL(*(impl_end + 1))))
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001439 {
1440 semsg(_(e_white_space_required_after_name_str), arg);
1441 goto early_ret;
1442 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001443 if (impl_end - arg == 0)
1444 {
1445 emsg(_(e_missing_name_after_implements));
1446 goto early_ret;
1447 }
1448
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001449 char_u *iname = vim_strnsave(arg, impl_end - arg);
1450 if (iname == NULL)
1451 goto early_ret;
1452 for (int i = 0; i < ga_impl.ga_len; ++i)
1453 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1454 {
1455 semsg(_(e_duplicate_interface_after_implements_str),
1456 iname);
1457 vim_free(iname);
1458 goto early_ret;
1459 }
1460 if (ga_add_string(&ga_impl, iname) == FAIL)
1461 {
1462 vim_free(iname);
1463 goto early_ret;
1464 }
1465 if (*impl_end != ',')
1466 {
1467 arg = skipwhite(impl_end);
1468 break;
1469 }
1470 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001471 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001472 }
1473 else
1474 {
1475 semsg(_(e_trailing_characters_str), arg);
1476early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001477 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001478 ga_clear_strings(&ga_impl);
1479 return;
1480 }
1481 }
1482
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001483 garray_T type_list; // list of pointers to allocated types
1484 ga_init2(&type_list, sizeof(type_T *), 10);
1485
Bram Moolenaard505d172022-12-18 21:42:55 +00001486 // Growarray with class members declared in the class.
1487 garray_T classmembers;
1488 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1489
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001490 // Growarray with functions declared in the class.
1491 garray_T classfunctions;
1492 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001493
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001494 // Growarray with object members declared in the class.
1495 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001496 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001497
1498 // Growarray with object methods declared in the class.
1499 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001500 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001501
1502 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001503 * Go over the body of the class/interface until "endclass" or
1504 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001505 */
1506 char_u *theline = NULL;
1507 int success = FALSE;
1508 for (;;)
1509 {
1510 vim_free(theline);
1511 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
1512 if (theline == NULL)
1513 break;
1514 char_u *line = skipwhite(theline);
1515
Bram Moolenaar418b5472022-12-20 13:38:22 +00001516 // Skip empty and comment lines.
1517 if (*line == NUL)
1518 continue;
1519 if (*line == '#')
1520 {
1521 if (vim9_bad_comment(line))
1522 break;
1523 continue;
1524 }
1525
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001526 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001527 char *end_name = is_class ? "endclass" : "endinterface";
1528 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001529 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001530 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001531 semsg(_(e_command_cannot_be_shortened_str), line);
1532 else if (*p == '|' || !ends_excmd2(line, p))
1533 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001534 else
1535 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001536 break;
1537 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001538 char *wrong_name = is_class ? "endinterface" : "endclass";
1539 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1540 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001541 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001542 break;
1543 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001544
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001545 int has_public = FALSE;
1546 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001547 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001548 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001549 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001550 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001551 break;
1552 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001553 if (!is_class)
1554 {
Yegappan Lakshmananb90e3bc2023-09-28 23:06:48 +02001555 emsg(_(e_public_variable_not_supported_in_interface));
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001556 break;
1557 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001558 has_public = TRUE;
1559 p = skipwhite(line + 6);
1560
Bram Moolenaard505d172022-12-18 21:42:55 +00001561 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001562 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001563 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001564 break;
1565 }
1566 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001567
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001568 int abstract_method = FALSE;
1569 char_u *pa = p;
1570 if (checkforcmd(&p, "abstract", 3))
1571 {
1572 if (STRNCMP(pa, "abstract", 8) != 0)
1573 {
1574 semsg(_(e_command_cannot_be_shortened_str), pa);
1575 break;
1576 }
1577
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01001578 if (!is_class)
1579 {
1580 // "abstract" not supported in an interface
1581 emsg(_(e_abstract_cannot_be_used_in_interface));
1582 break;
1583 }
1584
1585 if (!is_abstract)
1586 {
1587 semsg(_(e_abstract_method_in_concrete_class), pa);
1588 break;
1589 }
1590
Yegappan Lakshmanan5a539252023-11-04 09:42:46 +01001591 p = skipwhite(pa + 8);
1592 if (STRNCMP(p, "def", 3) != 0)
1593 {
1594 emsg(_(e_abstract_must_be_followed_by_def));
1595 break;
1596 }
1597
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01001598 abstract_method = TRUE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001599 }
1600
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001601 int has_static = FALSE;
1602 char_u *ps = p;
1603 if (checkforcmd(&p, "static", 4))
1604 {
1605 if (STRNCMP(ps, "static", 6) != 0)
1606 {
1607 semsg(_(e_command_cannot_be_shortened_str), ps);
1608 break;
1609 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001610
1611 if (!is_class)
1612 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001613 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001614 break;
1615 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001616 has_static = TRUE;
1617 p = skipwhite(ps + 6);
1618 }
1619
Bram Moolenaard505d172022-12-18 21:42:55 +00001620 // object members (public, read access, private):
1621 // "this._varname"
1622 // "this.varname"
1623 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001624 if (STRNCMP(p, "this", 4) == 0)
1625 {
1626 if (p[4] != '.' || !eval_isnamec1(p[5]))
1627 {
RestorerZ7fe8f432023-09-24 23:21:24 +02001628 semsg(_(e_invalid_object_variable_declaration_str), p);
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001629 break;
1630 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001631 if (has_static)
1632 {
1633 emsg(_(e_static_cannot_be_followed_by_this));
1634 break;
1635 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001636 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +00001637 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001638 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001639 char_u *init_expr = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001640 int has_type = FALSE;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001641
1642 if (!is_class && *varname == '_')
1643 {
1644 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01001645 semsg(_(e_protected_variable_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001646 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001647 break;
1648 }
1649
Bram Moolenaard505d172022-12-18 21:42:55 +00001650 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001651 &varname_end, &has_type, &type_list, &type,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001652 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001653 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001654 if (is_reserved_varname(varname, varname_end))
1655 {
1656 vim_free(init_expr);
1657 break;
1658 }
1659 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1660 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001661 {
1662 vim_free(init_expr);
1663 break;
1664 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001665 if (add_member(&objmembers, varname, varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001666 has_public, has_type, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001667 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001668 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001669 break;
1670 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001671 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001672
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001673 // constructors:
1674 // def new()
1675 // enddef
1676 // def newOther()
1677 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001678 // object methods and class functions:
1679 // def SomeMethod()
1680 // enddef
1681 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001682 // enddef
1683 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001684 // def <Tval> someMethod()
1685 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001686 else if (checkforcmd(&p, "def", 3))
1687 {
1688 exarg_T ea;
1689 garray_T lines_to_free;
1690
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001691 if (has_public)
1692 {
1693 // "public" keyword is not supported when defining an object or
1694 // class method
1695 emsg(_(e_public_keyword_not_supported_for_method));
1696 break;
1697 }
1698
1699 if (*p == NUL)
1700 {
1701 // No method name following def
1702 semsg(_(e_not_valid_command_in_class_str), line);
1703 break;
1704 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001705
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001706 CLEAR_FIELD(ea);
1707 ea.cmd = line;
1708 ea.arg = p;
1709 ea.cmdidx = CMD_def;
1710 ea.getline = eap->getline;
1711 ea.cookie = eap->cookie;
1712
1713 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001714 int class_flags;
1715 if (is_class)
1716 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
1717 else
1718 class_flags = CF_INTERFACE;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001719 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02001720 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001721 ga_clear_strings(&lines_to_free);
1722
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001723 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001724 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001725 char_u *name = uf->uf_name;
1726 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001727
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001728 if (!is_class && *name == '_')
1729 {
1730 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01001731 semsg(_(e_protected_method_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001732 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001733 func_clear_free(uf, FALSE);
1734 break;
1735 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001736 if (is_new && !is_valid_constructor(uf, is_abstract,
1737 has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001738 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001739 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001740 break;
1741 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001742
Bram Moolenaar58b40092023-01-11 15:59:05 +00001743 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001744 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001745 {
1746 success = FALSE;
1747 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001748 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001749 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001750
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001751 garray_T *fgap = has_static || is_new
1752 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001753 if (ga_grow(fgap, 1) == OK)
1754 {
1755 if (is_new)
1756 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001757
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001758 if (abstract_method)
1759 uf->uf_flags |= FC_ABSTRACT;
1760
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001761 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1762 ++fgap->ga_len;
1763 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001764 }
1765 }
1766
1767 // class members
1768 else if (has_static)
1769 {
1770 // class members (public, read access, private):
1771 // "static _varname"
1772 // "static varname"
1773 // "public static varname"
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001774 char_u *varname = p;
1775 char_u *varname_end = NULL;
1776 int has_type = FALSE;
1777 type_T *type = NULL;
1778 char_u *init_expr = NULL;
1779
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001780 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001781 &varname_end, &has_type, &type_list, &type,
Christian Brabandt6b366452023-11-19 18:59:52 +01001782 &init_expr) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001783 break;
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +02001784 if (is_reserved_varname(varname, varname_end))
1785 {
1786 vim_free(init_expr);
1787 break;
1788 }
1789 if (is_duplicate_variable(&classmembers, &objmembers, varname,
1790 varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001791 {
1792 vim_free(init_expr);
1793 break;
1794 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001795 if (add_member(&classmembers, varname, varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001796 has_public, has_type, type, init_expr) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001797 {
1798 vim_free(init_expr);
1799 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001800 }
1801 }
1802
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001803 else
1804 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001805 if (is_class)
1806 semsg(_(e_not_valid_command_in_class_str), line);
1807 else
1808 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001809 break;
1810 }
1811 }
1812 vim_free(theline);
1813
Bram Moolenaar83677162023-01-08 19:54:10 +00001814 class_T *extends_cl = NULL; // class from "extends" argument
1815
1816 /*
1817 * Check a few things before defining the class.
1818 */
1819
1820 // Check the "extends" class is valid.
1821 if (success && extends != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001822 success = validate_extends_class(extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00001823 VIM_CLEAR(extends);
1824
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001825 // Check the new object methods to make sure their access (public or
1826 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001827 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001828 success = validate_extends_methods(&objmethods, extends_cl);
1829
1830 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001831 // variables in the extended class lineage. If an interface is extending
1832 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001833 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001834 {
1835 if (is_class)
1836 success = extends_check_dup_members(&objmembers, extends_cl);
1837 else
1838 success = extends_check_intf_var_type(&objmembers, extends_cl);
1839 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001840
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001841 // When extending an abstract class, make sure all the abstract methods in
1842 // the parent class are implemented. If the current class is an abstract
1843 // class, then there is no need for this check.
1844 if (success && !is_abstract && extends_cl != NULL
1845 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001846 success = validate_abstract_class_methods(&classfunctions,
1847 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001848
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001849 class_T **intf_classes = NULL;
1850
Bram Moolenaar83677162023-01-08 19:54:10 +00001851 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001852 if (success && ga_impl.ga_len > 0)
1853 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001854 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1855
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001856 success = validate_implements_classes(&ga_impl, intf_classes,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02001857 &objmethods, &objmembers, extends_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001858 }
1859
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001860 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001861 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001862 success = check_func_arg_names(&classfunctions, &objmethods,
1863 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001864
Bram Moolenaareb533502022-12-14 15:06:11 +00001865 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001866 if (success)
1867 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001868 // "endclass" encountered without failures: Create the class.
1869
Bram Moolenaareb533502022-12-14 15:06:11 +00001870 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001871 if (cl == NULL)
1872 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001873 if (!is_class)
1874 cl->class_flags = CLASS_INTERFACE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001875 else if (is_abstract)
1876 cl->class_flags = CLASS_ABSTRACT;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001877
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001878 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001879 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001880 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001881 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001882
Bram Moolenaard0200c82023-01-28 15:19:40 +00001883 if (extends_cl != NULL)
1884 {
1885 cl->class_extends = extends_cl;
1886 extends_cl->class_flags |= CLASS_EXTENDED;
1887 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001888
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001889 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00001890 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001891 NULL,
1892 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00001893 &cl->class_class_members,
1894 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001895 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001896 extends_cl == NULL ? NULL
1897 : extends_cl->class_obj_members,
1898 extends_cl == NULL ? 0
1899 : extends_cl->class_obj_member_count,
1900 &cl->class_obj_members,
1901 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001902 goto cleanup;
1903
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001904 if (ga_impl.ga_len > 0)
1905 {
1906 // Move the "implements" names into the class.
1907 cl->class_interface_count = ga_impl.ga_len;
1908 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1909 if (cl->class_interfaces == NULL)
1910 goto cleanup;
1911 for (int i = 0; i < ga_impl.ga_len; ++i)
1912 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1913 VIM_CLEAR(ga_impl.ga_data);
1914 ga_impl.ga_len = 0;
1915
Bram Moolenaard0200c82023-01-28 15:19:40 +00001916 cl->class_interfaces_cl = intf_classes;
1917 intf_classes = NULL;
1918 }
1919
1920 if (cl->class_interface_count > 0 || extends_cl != NULL)
1921 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001922 // Add a method and member lookup table to each of the interface
1923 // classes.
1924 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1925 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001926 }
1927
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001928 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001929 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001930 add_class_members(cl, eap, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001931
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001932 int have_new = FALSE;
1933 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001934 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001935 {
1936 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1937 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001938 {
1939 have_new = TRUE;
1940 break;
1941 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001942 }
1943
1944 if (have_new)
1945 // The return type of new() is an object of class "cl"
1946 class_func->uf_ret_type->tt_class = cl;
1947 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001948 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001949 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001950
Bram Moolenaar58b40092023-01-11 15:59:05 +00001951 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001952 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1953 &objmethods) == FAIL)
1954 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001955
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001956 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001957 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001958 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001959 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001960 cl->class_type_list = type_list;
1961
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02001962 class_created(cl);
1963
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001964 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001965 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001966
1967 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001968 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02001969 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001970 typval_T tv;
1971 tv.v_type = VAR_CLASS;
1972 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001973 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001974 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001975 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001976 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001977 return;
1978 }
1979
1980cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00001981 if (cl != NULL)
1982 {
1983 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001984 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001985 if (cl->class_interfaces != NULL)
1986 {
1987 for (int i = 0; i < cl->class_interface_count; ++i)
1988 vim_free(cl->class_interfaces[i]);
1989 vim_free(cl->class_interfaces);
1990 }
1991 if (cl->class_interfaces_cl != NULL)
1992 {
1993 for (int i = 0; i < cl->class_interface_count; ++i)
1994 class_unref(cl->class_interfaces_cl[i]);
1995 vim_free(cl->class_interfaces_cl);
1996 }
Bram Moolenaareb533502022-12-14 15:06:11 +00001997 vim_free(cl->class_obj_members);
1998 vim_free(cl->class_obj_methods);
1999 vim_free(cl);
2000 }
2001
Bram Moolenaar83677162023-01-08 19:54:10 +00002002 vim_free(extends);
2003 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002004
2005 if (intf_classes != NULL)
2006 {
2007 for (int i = 0; i < ga_impl.ga_len; ++i)
2008 class_unref(intf_classes[i]);
2009 vim_free(intf_classes);
2010 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00002011 ga_clear_strings(&ga_impl);
2012
Bram Moolenaard505d172022-12-18 21:42:55 +00002013 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002014 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002015 garray_T *gap = round == 1 ? &classmembers : &objmembers;
2016 if (gap->ga_len == 0 || gap->ga_data == NULL)
2017 continue;
2018
2019 for (int i = 0; i < gap->ga_len; ++i)
2020 {
2021 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2022 vim_free(m->ocm_name);
2023 vim_free(m->ocm_init);
2024 }
2025 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002026 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002027
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002028 for (int i = 0; i < objmethods.ga_len; ++i)
2029 {
2030 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2031 func_clear_free(uf, FALSE);
2032 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002033 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002034
2035 for (int i = 0; i < classfunctions.ga_len; ++i)
2036 {
2037 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2038 func_clear_free(uf, FALSE);
2039 }
2040 ga_clear(&classfunctions);
2041
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002042 clear_type_list(&type_list);
2043}
2044
2045/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002046 * Find member "name" in class "cl", set "member_idx" to the member index and
2047 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002048 * When "is_object" is TRUE, then look for object members. Otherwise look for
2049 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002050 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002051 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002052 */
2053 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002054oc_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002055 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002056 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002057 char_u *name,
2058 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002059 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002060{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002061 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002062 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002063
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002064 *member_idx = -1; // not found (yet)
2065
2066 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2067 member_idx);
2068 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002069 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002070 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2071 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002072 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002073 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002074
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002075 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002076}
2077
2078/*
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002079 * Given a class or object variable index, return the variable type
2080 */
2081 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002082oc_member_type_by_idx(
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002083 class_T *cl,
2084 int is_object,
2085 int member_idx)
2086{
2087 ocmember_T *m;
2088 int member_count;
2089
2090 if (is_object)
2091 {
2092 m = cl->class_obj_members;
2093 member_count = cl->class_obj_member_count;
2094 }
2095 else
2096 {
2097 m = cl->class_class_members;
2098 member_count = cl->class_class_member_count;
2099 }
2100
2101 if (member_idx >= member_count)
2102 return NULL;
2103
2104 return m[member_idx].ocm_type;
2105}
2106
2107/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002108 * Handle ":enum" up to ":endenum".
2109 */
2110 void
2111ex_enum(exarg_T *eap UNUSED)
2112{
2113 // TODO
2114}
2115
2116/*
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002117 * Type aliases (:type)
2118 */
2119
2120 void
2121typealias_free(typealias_T *ta)
2122{
2123 // ta->ta_type is freed in clear_type_list()
2124 vim_free(ta->ta_name);
2125 vim_free(ta);
2126}
2127
2128 void
2129typealias_unref(typealias_T *ta)
2130{
2131 if (ta != NULL && --ta->ta_refcount <= 0)
2132 typealias_free(ta);
2133}
2134
2135/*
2136 * Handle ":type". Create an alias for a type specification.
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002137 */
2138 void
2139ex_type(exarg_T *eap UNUSED)
2140{
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002141 char_u *arg = eap->arg;
2142
2143 if (!current_script_is_vim9()
2144 || (cmdmod.cmod_flags & CMOD_LEGACY)
2145 || !getline_equal(eap->getline, eap->cookie, getsourceline))
2146 {
2147 emsg(_(e_type_can_only_be_defined_in_vim9_script));
2148 return;
2149 }
2150
2151 if (*arg == NUL)
2152 {
2153 emsg(_(e_missing_typealias_name));
2154 return;
2155 }
2156
2157 if (!ASCII_ISUPPER(*arg))
2158 {
2159 semsg(_(e_type_name_must_start_with_uppercase_letter_str), arg);
2160 return;
2161 }
2162
2163 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
2164 if (!IS_WHITE_OR_NUL(*name_end))
2165 {
2166 semsg(_(e_white_space_required_after_name_str), arg);
2167 return;
2168 }
2169 char_u *name_start = arg;
2170
2171 arg = skipwhite(name_end);
2172 if (*arg != '=')
2173 {
2174 semsg(_(e_missing_equal_str), arg);
2175 return;
2176 }
2177 if (!IS_WHITE_OR_NUL(*(arg + 1)))
2178 {
2179 semsg(_(e_white_space_required_after_str_str), "=", arg);
2180 return;
2181 }
2182 arg++;
2183 arg = skipwhite(arg);
2184
2185 if (*arg == NUL)
2186 {
2187 emsg(_(e_missing_typealias_type));
2188 return;
2189 }
2190
2191 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
2192 type_T *type = parse_type(&arg, &si->sn_type_list, TRUE);
2193 if (type == NULL)
2194 return;
2195
2196 if (*arg != NUL)
2197 {
2198 // some text after the type
2199 semsg(_(e_trailing_characters_str), arg);
2200 return;
2201 }
2202
2203 int cc = *name_end;
2204 *name_end = NUL;
2205
2206 typval_T tv;
2207 tv.v_type = VAR_UNKNOWN;
2208 if (eval_variable_import(name_start, &tv) == OK)
2209 {
2210 if (tv.v_type == VAR_TYPEALIAS)
2211 semsg(_(e_typealias_already_exists_for_str), name_start);
2212 else
2213 semsg(_(e_redefining_script_item_str), name_start);
2214 clear_tv(&tv);
2215 goto done;
2216 }
2217
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02002218 // Create a script-local variable for the type alias.
2219 if (type->tt_type != VAR_OBJECT)
2220 {
2221 tv.v_type = VAR_TYPEALIAS;
2222 tv.v_lock = 0;
2223 tv.vval.v_typealias = ALLOC_CLEAR_ONE(typealias_T);
2224 ++tv.vval.v_typealias->ta_refcount;
2225 tv.vval.v_typealias->ta_name = vim_strsave(name_start);
2226 tv.vval.v_typealias->ta_type = type;
2227 }
2228 else
2229 {
2230 // When creating a type alias for a class, use the class type itself to
2231 // create the type alias variable. This is needed to use the type
2232 // alias to invoke class methods (e.g. new()) and use class variables.
2233 tv.v_type = VAR_CLASS;
2234 tv.v_lock = 0;
2235 tv.vval.v_class = type->tt_class;
2236 ++tv.vval.v_class->class_refcount;
2237 }
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002238 set_var_const(name_start, current_sctx.sc_sid, NULL, &tv, FALSE,
2239 ASSIGN_CONST | ASSIGN_FINAL, 0);
2240
2241done:
2242 *name_end = cc;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002243}
2244
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002245/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002246 * Returns OK if a member variable named "name" is present in the class "cl".
2247 * Otherwise returns FAIL. If found, the member variable typval is set in
2248 * "rettv". If "is_object" is TRUE, then the object member variable table is
2249 * searched. Otherwise the class member variable table is searched.
2250 */
2251 static int
2252get_member_tv(
2253 class_T *cl,
2254 int is_object,
2255 char_u *name,
2256 size_t namelen,
2257 typval_T *rettv)
2258{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002259 ocmember_T *m;
2260 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002261
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002262 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2263 &m_idx);
2264 if (m == NULL)
2265 return FAIL;
2266
2267 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002268 {
Ernie Rael03042a22023-11-11 08:53:32 +01002269 emsg_var_cl_define(e_cannot_access_protected_variable_str,
Ernie Raele6c9aa52023-10-06 19:55:52 +02002270 m->ocm_name, 0, cl);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002271 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002272 }
2273
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002274 if (is_object)
2275 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002276 // The object only contains a pointer to the class, the member values
2277 // array follows right after that.
2278 object_T *obj = rettv->vval.v_object;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002279 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2280 copy_tv(tv, rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002281 object_unref(obj);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002282 }
2283 else
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002284 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002285 copy_tv(&cl->class_members_tv[m_idx], rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002286 class_unref(cl);
2287 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002288
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002289 return OK;
2290}
2291
2292/*
2293 * Call an object or class method "name" in class "cl". The method return
2294 * value is returned in "rettv".
2295 */
2296 static int
2297call_oc_method(
2298 class_T *cl,
2299 char_u *name,
2300 size_t len,
2301 char_u *name_end,
2302 evalarg_T *evalarg,
2303 char_u **arg,
2304 typval_T *rettv)
2305{
2306 ufunc_T *fp;
2307 typval_T argvars[MAX_FUNC_ARGS + 1];
2308 int argcount = 0;
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002309 ocmember_T *ocm = NULL;
2310 int m_idx;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002311
2312 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
2313 if (fp == NULL)
2314 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002315 // could be an object or class funcref variable
2316 ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
2317 if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
2318 {
2319 method_not_found_msg(cl, rettv->v_type, name, len);
2320 return FAIL;
2321 }
2322
2323 if (rettv->v_type == VAR_OBJECT)
2324 {
2325 // funcref object variable
2326 object_T *obj = rettv->vval.v_object;
2327 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2328 copy_tv(tv, rettv);
2329 }
2330 else
2331 // funcref class variable
2332 copy_tv(&cl->class_members_tv[m_idx], rettv);
2333 *arg = name_end;
2334 return OK;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002335 }
2336
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002337 if (ocm == NULL && *fp->uf_name == '_')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002338 {
2339 // Cannot access a private method outside of a class
Ernie Rael03042a22023-11-11 08:53:32 +01002340 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002341 return FAIL;
2342 }
2343
2344 char_u *argp = name_end;
2345 int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount);
2346 if (ret == FAIL)
2347 return FAIL;
2348
2349 funcexe_T funcexe;
2350 CLEAR_FIELD(funcexe);
2351 funcexe.fe_evaluate = TRUE;
2352 if (rettv->v_type == VAR_OBJECT)
2353 {
2354 funcexe.fe_object = rettv->vval.v_object;
2355 ++funcexe.fe_object->obj_refcount;
2356 }
2357
2358 // Clear the class or object after calling the function, in
2359 // case the refcount is one.
2360 typval_T tv_tofree = *rettv;
2361 rettv->v_type = VAR_UNKNOWN;
2362
2363 // Call the user function. Result goes into rettv;
2364 int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
2365 NULL);
2366
2367 // Clear the previous rettv and the arguments.
2368 clear_tv(&tv_tofree);
2369 for (int idx = 0; idx < argcount; ++idx)
2370 clear_tv(&argvars[idx]);
2371
2372 if (error != FCERR_NONE)
2373 {
2374 user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
2375 return FAIL;
2376 }
2377 *arg = argp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002378
2379 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002380}
2381
2382/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002383 * Evaluate what comes after a class:
2384 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002385 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002386 * - class constructor: SomeClass.new()
2387 * - object member: someObject.varname
2388 * - object method: someObject.SomeMethod()
2389 *
2390 * "*arg" points to the '.'.
2391 * "*arg" is advanced to after the member name or method call.
2392 *
2393 * Returns FAIL or OK.
2394 */
2395 int
2396class_object_index(
2397 char_u **arg,
2398 typval_T *rettv,
2399 evalarg_T *evalarg,
2400 int verbose UNUSED) // give error messages
2401{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002402 if (VIM_ISWHITE((*arg)[1]))
2403 {
2404 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
2405 return FAIL;
2406 }
2407
2408 ++*arg;
2409 char_u *name = *arg;
2410 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2411 if (name_end == name)
2412 return FAIL;
2413 size_t len = name_end - name;
2414
Ernie Raeld615a312023-10-05 20:28:16 +02002415 int did_emsg_save = did_emsg;
Bram Moolenaar552bdca2023-02-17 21:08:50 +00002416 class_T *cl;
2417 if (rettv->v_type == VAR_CLASS)
2418 cl = rettv->vval.v_class;
2419 else // VAR_OBJECT
2420 {
2421 if (rettv->vval.v_object == NULL)
2422 {
2423 emsg(_(e_using_null_object));
2424 return FAIL;
2425 }
2426 cl = rettv->vval.v_object->obj_class;
2427 }
2428
Bram Moolenaard13dd302023-03-11 20:56:35 +00002429 if (cl == NULL)
2430 {
2431 emsg(_(e_incomplete_type));
2432 return FAIL;
2433 }
2434
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002435 if (*name_end == '(')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002436 // Invoke the class or object method
2437 return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002438
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002439 else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002440 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002441 // Search in the object member variable table and the class member
2442 // variable table.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002443 int is_object = rettv->v_type == VAR_OBJECT;
2444 if (get_member_tv(cl, is_object, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002445 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002446 *arg = name_end;
2447 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002448 }
2449
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002450 // could be a class method or an object method
2451 int fidx;
2452 ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
2453 if (fp != NULL)
2454 {
2455 // Private methods are not accessible outside the class
2456 if (*name == '_')
2457 {
Ernie Rael03042a22023-11-11 08:53:32 +01002458 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002459 return FAIL;
2460 }
2461
2462 partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
2463 if (pt == NULL)
2464 return FAIL;
2465
2466 pt->pt_refcount = 1;
2467 if (is_object)
2468 {
2469 pt->pt_obj = rettv->vval.v_object;
2470 ++pt->pt_obj->obj_refcount;
2471 }
2472 pt->pt_auto = TRUE;
2473 pt->pt_func = fp;
2474 func_ptr_ref(pt->pt_func);
2475 rettv->v_type = VAR_PARTIAL;
2476 rettv->vval.v_partial = pt;
2477 *arg = name_end;
2478 return OK;
2479 }
2480
Ernie Raeld615a312023-10-05 20:28:16 +02002481 if (did_emsg == did_emsg_save)
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +02002482 member_not_found_msg(cl, rettv->v_type, name, len);
Bram Moolenaard505d172022-12-18 21:42:55 +00002483 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002484
2485 return FAIL;
2486}
2487
2488/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002489 * If "arg" points to a class or object method, return it.
2490 * Otherwise return NULL.
2491 */
2492 ufunc_T *
2493find_class_func(char_u **arg)
2494{
2495 char_u *name = *arg;
2496 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2497 if (name_end == name || *name_end != '.')
2498 return NULL;
2499
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002500 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002501 size_t len = name_end - name;
2502 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002503 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00002504 if (eval_variable(name, (int)len,
2505 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002506 return NULL;
2507 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00002508 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002509
2510 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
2511 : tv.vval.v_object->obj_class;
2512 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002513 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002514 char_u *fname = name_end + 1;
2515 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
2516 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00002517 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002518 len = fname_end - fname;
2519
Ernie Rael4d00b832023-09-11 19:54:42 +02002520 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002521
Bram Moolenaareb533502022-12-14 15:06:11 +00002522fail_after_eval:
2523 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002524 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002525}
2526
2527/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002528 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002529 * Returns -1, if the variable is not found.
2530 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002531 */
2532 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002533class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002534{
Ernie Rael4d00b832023-09-11 19:54:42 +02002535 int idx;
2536 class_member_lookup(cl, name, namelen, &idx);
2537 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002538}
2539
2540/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002541 * Returns a pointer to the class member variable "name" in the class "cl".
2542 * Returns NULL if the variable is not found.
2543 * The member variable index is set in "idx".
2544 */
2545 ocmember_T *
2546class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2547{
Ernie Rael4d00b832023-09-11 19:54:42 +02002548 ocmember_T *ret_m = NULL;
2549 int ret_idx = -1;
2550 for (int i = 0; i < cl->class_class_member_count; ++i)
2551 {
2552 ocmember_T *m = &cl->class_class_members[i];
2553 if (namelen)
2554 {
2555 if (STRNCMP(name, m->ocm_name, namelen) == 0
2556 && m->ocm_name[namelen] == NUL)
2557 {
2558 ret_m = m;
2559 ret_idx = i;
2560 break;
2561 }
2562 }
2563 else if (STRCMP(name, m->ocm_name) == 0)
2564 {
2565 ret_m = m;
2566 ret_idx = i;
2567 break;
2568 }
2569 }
2570 if (idx != NULL)
2571 *idx = ret_idx;
2572 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002573}
2574
2575/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002576 * Returns a pointer to the class method "name" in class "cl".
2577 * Returns NULL if the method is not found.
2578 * The method index is set in "idx".
2579 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002580 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002581class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2582{
Ernie Rael4d00b832023-09-11 19:54:42 +02002583 ufunc_T *ret_fp = NULL;
2584 int ret_idx = -1;
2585 for (int i = 0; i < cl->class_class_function_count; ++i)
2586 {
2587 ufunc_T *fp = cl->class_class_functions[i];
2588 char_u *ufname = (char_u *)fp->uf_name;
2589 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2590 {
2591 ret_fp = fp;
2592 ret_idx = i;
2593 break;
2594 }
2595 }
2596 if (idx != NULL)
2597 *idx = ret_idx;
2598 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002599}
2600
2601/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002602 * Returns the index of class method "name" in the class "cl".
2603 * Returns -1, if the method is not found.
2604 */
2605 int
2606class_method_idx(class_T *cl, char_u *name, size_t namelen)
2607{
2608 int idx;
2609 class_method_lookup(cl, name, namelen, &idx);
2610 return idx;
2611}
2612
2613/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002614 * Returns the index of object member variable "name" in the class "cl".
2615 * Returns -1, if the variable is not found.
2616 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
2617 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002618 static int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002619object_member_idx(class_T *cl, char_u *name, size_t namelen)
2620{
Ernie Rael4d00b832023-09-11 19:54:42 +02002621 int idx;
2622 object_member_lookup(cl, name, namelen, &idx);
2623 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002624}
2625
2626/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002627 * Returns a pointer to the object member variable "name" in the class "cl".
2628 * Returns NULL if the variable is not found.
2629 * The object member variable index is set in "idx".
2630 */
2631 ocmember_T *
2632object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2633{
Ernie Rael4d00b832023-09-11 19:54:42 +02002634 ocmember_T *ret_m = NULL;
2635 int ret_idx = -1;
2636 for (int i = 0; i < cl->class_obj_member_count; ++i)
2637 {
2638 ocmember_T *m = &cl->class_obj_members[i];
2639 if (namelen)
2640 {
2641 if (STRNCMP(name, m->ocm_name, namelen) == 0
2642 && m->ocm_name[namelen] == NUL)
2643 {
2644 ret_m = m;
2645 ret_idx = i;
2646 break;
2647 }
2648 }
2649 else if (STRCMP(name, m->ocm_name) == 0)
2650 {
2651 ret_m = m;
2652 ret_idx = i;
2653 break;
2654 }
2655 }
2656 if (idx != NULL)
2657 *idx = ret_idx;
2658 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002659}
2660
2661/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002662 * Returns a pointer to the object method "name" in class "cl".
2663 * Returns NULL if the method is not found.
2664 * The object method index is set in "idx".
2665 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002666 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002667object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2668{
Ernie Rael4d00b832023-09-11 19:54:42 +02002669 ufunc_T *ret_fp = NULL;
2670 int ret_idx = -1;
2671 for (int i = 0; i < cl->class_obj_method_count; ++i)
2672 {
2673 ufunc_T *fp = cl->class_obj_methods[i];
2674 // Use a separate pointer to avoid that ASAN complains about
2675 // uf_name[] only being 4 characters.
2676 char_u *ufname = (char_u *)fp->uf_name;
2677 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2678 {
2679 ret_fp = fp;
2680 ret_idx = i;
2681 break;
2682 }
2683 }
2684 if (idx != NULL)
2685 *idx = ret_idx;
2686 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002687}
2688
2689/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002690 * Returns the index of object method "name" in the class "cl".
2691 * Returns -1, if the method is not found.
2692 */
2693 int
2694object_method_idx(class_T *cl, char_u *name, size_t namelen)
2695{
2696 int idx;
2697 object_method_lookup(cl, name, namelen, &idx);
2698 return idx;
2699}
2700
2701/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002702 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
2703 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
2704 * object member variable.
2705 *
2706 * Returns a pointer to the member variable structure if variable is found.
2707 * Otherwise returns NULL. The member variable index is set in "*idx".
2708 */
2709 ocmember_T *
2710member_lookup(
2711 class_T *cl,
2712 vartype_T v_type,
2713 char_u *name,
2714 size_t namelen,
2715 int *idx)
2716{
2717 if (v_type == VAR_CLASS)
2718 return class_member_lookup(cl, name, namelen, idx);
2719 else
2720 return object_member_lookup(cl, name, namelen, idx);
2721}
2722
2723/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02002724 * Find the class that defines the named member. Look up the hierarchy
2725 * starting at "cl".
2726 *
2727 * Return the class that defines the member "name", else NULL.
2728 * Fill in "p_m", if specified, for ocmember_T in found class.
2729 */
2730// NOTE: if useful for something could also indirectly return vartype and idx.
2731 static class_T *
2732class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m)
2733{
2734 class_T *cl_found = NULL;
2735 vartype_T vartype = VAR_UNKNOWN;
2736 ocmember_T *m_found = NULL;
2737
2738 len = len != 0 ? len : STRLEN(name);
2739
2740 // Loop assumes if member is not defined in "cl", then it is not
2741 // defined in any super class; the last class where it's found is the
2742 // class where it is defined. Once the vartype is found, the other
2743 // type is no longer checked.
2744 for (class_T *super = cl; super != NULL; super = super->class_extends)
2745 {
2746 class_T *cl_tmp = NULL;
2747 ocmember_T *m = NULL;
2748 if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT)
2749 {
2750 if ((m = object_member_lookup(super, name, len, NULL)) != NULL)
2751 {
2752 cl_tmp = super;
2753 vartype = VAR_OBJECT;
2754 }
2755 }
2756 if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS)
2757 {
2758 if (( m = class_member_lookup(super, name, len, NULL)) != NULL)
2759 {
2760 cl_tmp = super;
2761 vartype = VAR_OBJECT;
2762 }
2763 }
2764 if (cl_tmp == NULL)
2765 break; // member is not in this or any super class.
2766 cl_found = cl_tmp;
2767 m_found = m;
2768 }
2769 if (p_m != NULL)
2770 *p_m = m_found;
2771 return cl_found;
2772}
2773
2774/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002775 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
2776 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
2777 *
2778 * Returns a pointer to the method structure if variable is found.
2779 * Otherwise returns NULL. The method variable index is set in "*idx".
2780 */
2781 ufunc_T *
2782method_lookup(
2783 class_T *cl,
2784 vartype_T v_type,
2785 char_u *name,
2786 size_t namelen,
2787 int *idx)
2788{
2789 if (v_type == VAR_CLASS)
2790 return class_method_lookup(cl, name, namelen, idx);
2791 else
2792 return object_method_lookup(cl, name, namelen, idx);
2793}
2794
2795/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00002796 * Return TRUE if current context "cctx_arg" is inside class "cl".
2797 * Return FALSE if not.
2798 */
2799 int
2800inside_class(cctx_T *cctx_arg, class_T *cl)
2801{
2802 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002803 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002804 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002805 return TRUE;
2806 return FALSE;
2807}
2808
2809/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002810 * Make a copy of an object.
2811 */
2812 void
2813copy_object(typval_T *from, typval_T *to)
2814{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002815 if (from->vval.v_object == NULL)
2816 to->vval.v_object = NULL;
2817 else
2818 {
2819 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002820 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002821 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002822}
2823
2824/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002825 * Make a copy of a class.
2826 */
2827 void
2828copy_class(typval_T *from, typval_T *to)
2829{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002830 if (from->vval.v_class == NULL)
2831 to->vval.v_class = NULL;
2832 else
2833 {
2834 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002835 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02002836 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002837}
2838
2839/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002840 * Free the class "cl" and its contents.
2841 */
2842 static void
2843class_free(class_T *cl)
2844{
2845 // Freeing what the class contains may recursively come back here.
2846 // Clear "class_name" first, if it is NULL the class does not need to
2847 // be freed.
2848 VIM_CLEAR(cl->class_name);
2849
2850 class_unref(cl->class_extends);
2851
2852 for (int i = 0; i < cl->class_interface_count; ++i)
2853 {
2854 vim_free(((char_u **)cl->class_interfaces)[i]);
2855 if (cl->class_interfaces_cl[i] != NULL)
2856 class_unref(cl->class_interfaces_cl[i]);
2857 }
2858 vim_free(cl->class_interfaces);
2859 vim_free(cl->class_interfaces_cl);
2860
2861 itf2class_T *next;
2862 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
2863 {
2864 next = i2c->i2c_next;
2865 vim_free(i2c);
2866 }
2867
2868 for (int i = 0; i < cl->class_class_member_count; ++i)
2869 {
2870 ocmember_T *m = &cl->class_class_members[i];
2871 vim_free(m->ocm_name);
2872 vim_free(m->ocm_init);
2873 if (cl->class_members_tv != NULL)
2874 clear_tv(&cl->class_members_tv[i]);
2875 }
2876 vim_free(cl->class_class_members);
2877 vim_free(cl->class_members_tv);
2878
2879 for (int i = 0; i < cl->class_obj_member_count; ++i)
2880 {
2881 ocmember_T *m = &cl->class_obj_members[i];
2882 vim_free(m->ocm_name);
2883 vim_free(m->ocm_init);
2884 }
2885 vim_free(cl->class_obj_members);
2886
2887 for (int i = 0; i < cl->class_class_function_count; ++i)
2888 {
2889 ufunc_T *uf = cl->class_class_functions[i];
2890 func_clear_free(uf, FALSE);
2891 }
2892 vim_free(cl->class_class_functions);
2893
2894 for (int i = 0; i < cl->class_obj_method_count; ++i)
2895 {
2896 ufunc_T *uf = cl->class_obj_methods[i];
2897 func_clear_free(uf, FALSE);
2898 }
2899 vim_free(cl->class_obj_methods);
2900
2901 clear_type_list(&cl->class_type_list);
2902
2903 class_cleared(cl);
2904
2905 vim_free(cl);
2906}
2907
2908/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002909 * Unreference a class. Free it when the reference count goes down to zero.
2910 */
2911 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002912class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002913{
Bram Moolenaard505d172022-12-18 21:42:55 +00002914 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002915 class_free(cl);
2916}
2917
2918/*
2919 * Go through the list of all classes and free items without "copyID".
2920 */
2921 int
2922class_free_nonref(int copyID)
2923{
2924 int did_free = FALSE;
2925
2926 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002927 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002928 next_nonref_class = cl->class_next_used;
2929 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002930 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002931 // Free the class and items it contains.
2932 class_free(cl);
2933 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002934 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002935 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002936
2937 next_nonref_class = NULL;
2938 return did_free;
2939}
2940
2941 int
2942set_ref_in_classes(int copyID)
2943{
2944 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
2945 set_ref_in_item_class(cl, copyID, NULL, NULL);
2946
2947 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002948}
2949
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002950static object_T *first_object = NULL;
2951
2952/*
2953 * Call this function when an object has been created. It will be added to the
2954 * list headed by "first_object".
2955 */
2956 void
2957object_created(object_T *obj)
2958{
2959 if (first_object != NULL)
2960 {
2961 obj->obj_next_used = first_object;
2962 first_object->obj_prev_used = obj;
2963 }
2964 first_object = obj;
2965}
2966
2967/*
2968 * Call this function when an object has been cleared and is about to be freed.
2969 * It is removed from the list headed by "first_object".
2970 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002971 static void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002972object_cleared(object_T *obj)
2973{
2974 if (obj->obj_next_used != NULL)
2975 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
2976 if (obj->obj_prev_used != NULL)
2977 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
2978 else if (first_object == obj)
2979 first_object = obj->obj_next_used;
2980}
2981
2982/*
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002983 * Free the contents of an object ignoring the reference count.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002984 */
2985 static void
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002986object_free_contents(object_T *obj)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002987{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002988 class_T *cl = obj->obj_class;
2989
2990 if (!cl)
2991 return;
2992
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002993 // Avoid a recursive call, it can happen if "obj" has a circular reference.
2994 obj->obj_refcount = INT_MAX;
2995
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002996 // the member values are just after the object structure
2997 typval_T *tv = (typval_T *)(obj + 1);
2998 for (int i = 0; i < cl->class_obj_member_count; ++i)
2999 clear_tv(tv + i);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003000}
3001
3002 static void
3003object_free_object(object_T *obj)
3004{
3005 class_T *cl = obj->obj_class;
3006
3007 if (!cl)
3008 return;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003009
3010 // Remove from the list headed by "first_object".
3011 object_cleared(obj);
3012
3013 vim_free(obj);
3014 class_unref(cl);
3015}
3016
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003017 static void
3018object_free(object_T *obj)
3019{
3020 if (in_free_unref_items)
3021 return;
3022
3023 object_free_contents(obj);
3024 object_free_object(obj);
3025}
3026
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003027/*
3028 * Unreference an object.
3029 */
3030 void
3031object_unref(object_T *obj)
3032{
3033 if (obj != NULL && --obj->obj_refcount <= 0)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003034 object_free(obj);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003035}
3036
3037/*
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003038 * Go through the list of all objects and free items without "copyID".
3039 */
3040 int
3041object_free_nonref(int copyID)
3042{
3043 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003044
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003045 for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003046 {
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003047 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3048 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003049 // Free the object contents. Object itself will be freed later.
3050 object_free_contents(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003051 did_free = TRUE;
3052 }
3053 }
3054
3055 return did_free;
3056}
3057
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003058 void
3059object_free_items(int copyID)
3060{
3061 object_T *obj_next;
3062
3063 for (object_T *obj = first_object; obj != NULL; obj = obj_next)
3064 {
3065 obj_next = obj->obj_next_used;
3066 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3067 object_free_object(obj);
3068 }
3069}
3070
LemonBoyafe04662023-08-23 21:08:11 +02003071/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003072 * Output message which takes a variable name and the class that defines it.
3073 * "cl" is that class where the name was found. Search "cl"'s hierarchy to
3074 * find the defining class.
3075 */
3076 void
3077emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl)
3078{
3079 ocmember_T *m;
3080 class_T *cl_def = class_defining_member(cl, name, len, &m);
3081 if (cl_def != NULL)
3082 semsg(_(msg), m->ocm_name, cl_def->class_name);
3083 else
3084 emsg(_(e_internal_error_please_report_a_bug));
3085}
3086
3087/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003088 * Echo a class or object method not found message.
3089 */
3090 void
3091method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3092{
3093 char_u *method_name = vim_strnsave(name, len);
3094 if ((v_type == VAR_OBJECT)
3095 && (class_method_idx(cl, name, len) >= 0))
3096 {
3097 // If this is a class method, then give a different error
3098 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003099 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003100 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003101 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003102 method_name, cl->class_name);
3103 }
3104 else if ((v_type == VAR_CLASS)
3105 && (object_method_idx(cl, name, len) >= 0))
3106 {
3107 // If this is an object method, then give a different error
3108 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003109 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003110 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003111 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003112 method_name, cl->class_name);
3113 }
3114 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003115 semsg(_(e_method_not_found_on_class_str_str), method_name,
3116 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003117 vim_free(method_name);
3118}
3119
3120/*
3121 * Echo a class or object member not found message.
3122 */
3123 void
3124member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3125{
3126 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
3127
3128 if (v_type == VAR_OBJECT)
3129 {
3130 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003131 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003132 varname, cl->class_name);
3133 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003134 semsg(_(e_variable_not_found_on_object_str_str), varname,
3135 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003136 }
3137 else
3138 {
3139 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003140 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003141 varname, cl->class_name);
3142 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003143 semsg(_(e_class_variable_str_not_found_in_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003144 varname, cl->class_name);
3145 }
3146 vim_free(varname);
3147}
3148
3149/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02003150 * Return TRUE when the class "cl", its base class or one of the implemented
3151 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02003152 */
3153 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003154class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02003155{
3156 if (cl == other_cl)
3157 return TRUE;
3158
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003159 // Recursively check the base classes.
3160 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02003161 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003162 if (cl == other_cl)
3163 return TRUE;
3164 // Check the implemented interfaces and the super interfaces
3165 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003166 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003167 class_T *intf = cl->class_interfaces_cl[i];
3168 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003169 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003170 if (intf == other_cl)
3171 return TRUE;
3172 // check the super interfaces
3173 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003174 }
3175 }
LemonBoyafe04662023-08-23 21:08:11 +02003176 }
3177
3178 return FALSE;
3179}
3180
3181/*
Ernie Rael2025af12023-12-12 16:58:00 +01003182 * "instanceof(object, classinfo, ...)" function
LemonBoyafe04662023-08-23 21:08:11 +02003183 */
3184 void
3185f_instanceof(typval_T *argvars, typval_T *rettv)
3186{
3187 typval_T *object_tv = &argvars[0];
3188 typval_T *classinfo_tv = &argvars[1];
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02003189 class_T *c;
LemonBoyafe04662023-08-23 21:08:11 +02003190
3191 rettv->vval.v_number = VVAL_FALSE;
3192
3193 if (check_for_object_arg(argvars, 0) == FAIL
Ernie Rael2025af12023-12-12 16:58:00 +01003194 || check_for_class_or_typealias_args(argvars, 1) == FAIL)
LemonBoyafe04662023-08-23 21:08:11 +02003195 return;
3196
Ernie Rael3da696d2023-09-19 20:14:18 +02003197 if (object_tv->vval.v_object == NULL)
3198 return;
3199
Ernie Rael2025af12023-12-12 16:58:00 +01003200 for (; classinfo_tv->v_type != VAR_UNKNOWN; ++classinfo_tv)
LemonBoyafe04662023-08-23 21:08:11 +02003201 {
Ernie Rael2025af12023-12-12 16:58:00 +01003202 if (classinfo_tv->v_type == VAR_TYPEALIAS)
3203 c = classinfo_tv->vval.v_typealias->ta_type->tt_class;
3204 else
3205 c = classinfo_tv->vval.v_class;
3206
3207 if (class_instance_of(object_tv->vval.v_object->obj_class, c))
LemonBoyafe04662023-08-23 21:08:11 +02003208 {
Ernie Rael2025af12023-12-12 16:58:00 +01003209 rettv->vval.v_number = VVAL_TRUE;
3210 return;
LemonBoyafe04662023-08-23 21:08:11 +02003211 }
3212 }
LemonBoyafe04662023-08-23 21:08:11 +02003213}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00003214
3215#endif // FEAT_EVAL