blob: 7227c3dc28882a39e4bd509462c75d39ac9fc2d8 [file] [log] [blame]
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * vim9class.c: Vim9 script class support
12 */
13
14#define USING_FLOAT_STUFF
15#include "vim.h"
16
17#if defined(FEAT_EVAL) || defined(PROTO)
18
19// When not generating protos this is included in proto.h
20#ifdef PROTO
21# include "vim9.h"
22#endif
23
Yegappan Lakshmanane651e112023-09-04 07:51:01 +020024static class_T *first_class = NULL;
25static class_T *next_nonref_class = NULL;
26
27/*
28 * Call this function when a class has been created. It will be added to the
29 * list headed by "first_class".
30 */
31 static void
32class_created(class_T *cl)
33{
34 if (first_class != NULL)
35 {
36 cl->class_next_used = first_class;
37 first_class->class_prev_used = cl;
38 }
39 first_class = cl;
40}
41
42/*
43 * Call this function when a class has been cleared and is about to be freed.
44 * It is removed from the list headed by "first_class".
45 */
46 static void
47class_cleared(class_T *cl)
48{
49 if (cl->class_next_used != NULL)
50 cl->class_next_used->class_prev_used = cl->class_prev_used;
51 if (cl->class_prev_used != NULL)
52 cl->class_prev_used->class_next_used = cl->class_next_used;
53 else if (first_class == cl)
54 first_class = cl->class_next_used;
55
56 // update the next class to check if needed
57 if (cl == next_nonref_class)
58 next_nonref_class = cl->class_next_used;
59}
60
Bram Moolenaarc1c365c2022-12-04 20:13:24 +000061/*
Bram Moolenaard505d172022-12-18 21:42:55 +000062 * Parse a member declaration, both object and class member.
63 * Returns OK or FAIL. When OK then "varname_end" is set to just after the
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +020064 * variable name and "type_ret" is set to the declared or detected type.
Bram Moolenaard505d172022-12-18 21:42:55 +000065 * "init_expr" is set to the initialisation expression (allocated), if there is
Bram Moolenaar554d0312023-01-05 19:59:18 +000066 * one. For an interface "init_expr" is NULL.
Bram Moolenaard505d172022-12-18 21:42:55 +000067 */
68 static int
69parse_member(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020070 exarg_T *eap,
71 char_u *line,
72 char_u *varname,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020073 int has_public, // TRUE if "public" seen before "varname"
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020074 char_u **varname_end,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +020075 int *has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020076 garray_T *type_list,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020077 type_T **type_ret,
78 char_u **init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +000079{
80 *varname_end = to_name_end(varname, FALSE);
81 if (*varname == '_' && has_public)
82 {
RestorerZ7fe8f432023-09-24 23:21:24 +020083 semsg(_(e_public_variable_name_cannot_start_with_underscore_str), line);
Bram Moolenaard505d172022-12-18 21:42:55 +000084 return FAIL;
85 }
86
87 char_u *colon = skipwhite(*varname_end);
88 char_u *type_arg = colon;
89 type_T *type = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +020090 *has_type = FALSE;
Bram Moolenaard505d172022-12-18 21:42:55 +000091 if (*colon == ':')
92 {
93 if (VIM_ISWHITE(**varname_end))
94 {
95 semsg(_(e_no_white_space_allowed_before_colon_str), varname);
96 return FAIL;
97 }
98 if (!VIM_ISWHITE(colon[1]))
99 {
100 semsg(_(e_white_space_required_after_str_str), ":", varname);
101 return FAIL;
102 }
103 type_arg = skipwhite(colon + 1);
104 type = parse_type(&type_arg, type_list, TRUE);
105 if (type == NULL)
106 return FAIL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200107 *has_type = TRUE;
Bram Moolenaard505d172022-12-18 21:42:55 +0000108 }
109
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200110 char_u *init_arg = skipwhite(type_arg);
111 if (type == NULL && *init_arg != '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000112 {
113 emsg(_(e_type_or_initialization_required));
114 return FAIL;
115 }
116
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200117 if (init_expr == NULL && *init_arg == '=')
Bram Moolenaard505d172022-12-18 21:42:55 +0000118 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200119 emsg(_(e_cannot_initialize_variable_in_interface));
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200120 return FAIL;
121 }
122
123 if (*init_arg == '=')
124 {
125 evalarg_T evalarg;
126 char_u *expr_start, *expr_end;
127
128 if (!VIM_ISWHITE(init_arg[-1]) || !VIM_ISWHITE(init_arg[1]))
Bram Moolenaard505d172022-12-18 21:42:55 +0000129 {
130 semsg(_(e_white_space_required_before_and_after_str_at_str),
131 "=", type_arg);
132 return FAIL;
133 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200134 init_arg = skipwhite(init_arg + 1);
Bram Moolenaard505d172022-12-18 21:42:55 +0000135
Bram Moolenaard505d172022-12-18 21:42:55 +0000136 fill_evalarg_from_eap(&evalarg, eap, FALSE);
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200137 (void)skip_expr_concatenate(&init_arg, &expr_start, &expr_end, &evalarg);
Bram Moolenaard505d172022-12-18 21:42:55 +0000138
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +0200139 // No type specified for the member. Set it to "any" and the correct
140 // type will be set when the object is instantiated.
Bram Moolenaard505d172022-12-18 21:42:55 +0000141 if (type == NULL)
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200142 type = &t_any;
Bram Moolenaard505d172022-12-18 21:42:55 +0000143
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200144 *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
145 // Free the memory pointed by expr_start.
Bram Moolenaard505d172022-12-18 21:42:55 +0000146 clear_evalarg(&evalarg, NULL);
147 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200148 else if (!valid_declaration_type(type))
Bram Moolenaard505d172022-12-18 21:42:55 +0000149 return FAIL;
150
151 *type_ret = type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000152 return OK;
153}
154
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100155typedef struct oc_newmember_S oc_newmember_T;
156struct oc_newmember_S
157{
158 garray_T *gap;
159 char_u *varname;
160 char_u *varname_end;
161 int has_public;
162 int has_final;
163 int has_type;
164 type_T *type;
165 char_u *init_expr;
166};
167
Bram Moolenaard505d172022-12-18 21:42:55 +0000168/*
169 * Add a member to an object or a class.
170 * Returns OK when successful, "init_expr" will be consumed then.
171 * Returns FAIL otherwise, caller might need to free "init_expr".
172 */
173 static int
174add_member(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200175 garray_T *gap,
176 char_u *varname,
177 char_u *varname_end,
178 int has_public,
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100179 int has_final,
180 int has_const,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +0200181 int has_type,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200182 type_T *type,
183 char_u *init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +0000184{
185 if (ga_grow(gap, 1) == FAIL)
186 return FAIL;
187 ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
188 m->ocm_name = vim_strnsave(varname, varname_end - varname);
=?UTF-8?q?Ola=20S=C3=B6der?=d8742472023-03-05 13:12:32 +0000189 m->ocm_access = has_public ? VIM_ACCESS_ALL
190 : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +0100191 if (has_final)
192 m->ocm_flags |= OCMFLAG_FINAL;
193 if (has_const)
194 m->ocm_flags |= OCMFLAG_CONST;
195 if (has_type)
196 m->ocm_flags |= OCMFLAG_HAS_TYPE;
Bram Moolenaard505d172022-12-18 21:42:55 +0000197 m->ocm_type = type;
198 if (init_expr != NULL)
199 m->ocm_init = init_expr;
200 ++gap->ga_len;
201 return OK;
202}
203
204/*
205 * Move the class or object members found while parsing a class into the class.
206 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000207 * "parent_members" points to the members in the parent class (if any)
208 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000209 * "members" will be set to the newly allocated array of members and
210 * "member_count" set to the number of members.
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100211 * Returns OK on success and FAIL on memory allocation failure.
Bram Moolenaard505d172022-12-18 21:42:55 +0000212 */
213 static int
214add_members_to_class(
215 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000216 ocmember_T *parent_members,
217 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000218 ocmember_T **members,
219 int *member_count)
220{
Bram Moolenaar83677162023-01-08 19:54:10 +0000221 *member_count = parent_count + gap->ga_len;
222 *members = *member_count == 0 ? NULL
223 : ALLOC_MULT(ocmember_T, *member_count);
224 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000225 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000226 for (int i = 0; i < parent_count; ++i)
227 {
228 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000229 ocmember_T *m = *members + i;
230 *m = parent_members[i];
231 m->ocm_name = vim_strsave(m->ocm_name);
232 if (m->ocm_init != NULL)
233 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000234 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000235 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000236 // new members are moved
237 mch_memmove(*members + parent_count,
238 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000239 VIM_CLEAR(gap->ga_data);
240 return OK;
241}
242
243/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000244 * Convert a member index "idx" of interface "itf" to the member index of class
245 * "cl" implementing that interface.
246 */
247 int
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200248object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000249{
Ernie Rael18143d32023-09-04 22:30:41 +0200250 if (idx >= (is_method ? itf->class_obj_method_count
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200251 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000252 {
253 siemsg("index %d out of range for interface %s", idx, itf->class_name);
254 return 0;
255 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200256
257 // If "cl" is the interface or the class that is extended, then the method
258 // index can be used directly and there is no need to search for the method
259 // index in one of the child classes.
260 if (cl == itf)
261 return idx;
262
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200263 itf2class_T *i2c = NULL;
264 int searching = TRUE;
265 int method_offset = 0;
266
Ernie Raelcf138d42023-09-06 20:45:03 +0200267 for (class_T *super = cl; super != NULL && searching;
268 super = super->class_extends)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200269 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200270 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200271 {
Ernie Raelcf138d42023-09-06 20:45:03 +0200272 if (i2c->i2c_class == super && i2c->i2c_is_method == is_method)
273 {
274 searching = FALSE;
275 break;
276 }
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200277 }
278 if (searching && is_method)
279 // The parent class methods are stored after the current class
280 // methods.
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200281 method_offset += super->class_obj_method_count_child;
Yegappan Lakshmanancc0bcf42023-09-08 19:12:03 +0200282 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000283 if (i2c == NULL)
284 {
285 siemsg("class %s not found on interface %s",
286 cl->class_name, itf->class_name);
287 return 0;
288 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +0200289
Yegappan Lakshmanan5a05d372023-09-29 19:43:11 +0200290 // A table follows the i2c for the class
291 int *table = (int *)(i2c + 1);
292 // "method_offset" is 0, if method is in the current class. If method
293 // is in a parent class, then it is non-zero.
294 return table[idx] + method_offset;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000295}
296
297/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200298 * Check whether a class named "extends_name" is present. If the class is
299 * valid, then "extends_clp" is set with the class pointer.
300 * Returns TRUE if the class name "extends_names" is a valid class.
301 */
302 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200303validate_extends_class(
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100304 class_T *cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200305 char_u *extends_name,
306 class_T **extends_clp,
307 int is_class)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200308{
309 typval_T tv;
310 int success = FALSE;
311
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +0100312 if (STRCMP(cl->class_name, extends_name) == 0)
313 {
314 semsg(_(e_cannot_extend_str), extends_name);
315 return success;
316 }
317
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200318 tv.v_type = VAR_UNKNOWN;
319 if (eval_variable_import(extends_name, &tv) == FAIL)
320 {
321 semsg(_(e_class_name_not_found_str), extends_name);
322 return success;
323 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200324
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200325 if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
326 || (is_class
327 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
328 || (!is_class
329 && (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0))
330 // a interface cannot extend a class and a class cannot extend an
331 // interface.
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200332 semsg(_(e_cannot_extend_str), extends_name);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200333 else
334 {
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200335 class_T *extends_cl = tv.vval.v_class;
336 ++extends_cl->class_refcount;
337 *extends_clp = extends_cl;
338 success = TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200339 }
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200340 clear_tv(&tv);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200341
342 return success;
343}
344
345/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200346 * Check method names in the parent class lineage to make sure the access is
347 * the same for overridden methods.
348 */
349 static int
350validate_extends_methods(
351 garray_T *objmethods_gap,
352 class_T *extends_cl)
353{
354 class_T *super = extends_cl;
355 int method_count = objmethods_gap->ga_len;
356 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
357
358 while (super != NULL)
359 {
360 int extends_method_count = super->class_obj_method_count_child;
361 if (extends_method_count == 0)
362 {
363 super = super->class_extends;
364 continue;
365 }
366
367 ufunc_T **extends_methods = super->class_obj_methods;
368
369 for (int i = 0; i < extends_method_count; i++)
370 {
371 char_u *pstr = extends_methods[i]->uf_name;
372 int extends_private = (*pstr == '_');
373 if (extends_private)
374 pstr++;
375
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200376 // When comparing the method names, ignore the access type (public
377 // and private methods are considered the same).
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200378 for (int j = 0; j < method_count; j++)
379 {
380 char_u *qstr = cl_fp[j]->uf_name;
381 int priv_method = (*qstr == '_');
382 if (priv_method)
383 qstr++;
384 if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
385 {
386 // Method access is different between the super class and
387 // the subclass
388 semsg(_(e_method_str_of_class_str_has_different_access),
389 cl_fp[j]->uf_name, super->class_name);
390 return FALSE;
391 }
392 }
393 }
394 super = super->class_extends;
395 }
396
397 return TRUE;
398}
399
400/*
401 * Check whether a object member variable in "objmembers_gap" is a duplicate of
402 * a member in any of the extended parent class lineage. Returns TRUE if there
403 * are no duplicates.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200404 */
405 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200406extends_check_dup_members(
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200407 garray_T *objmembers_gap,
408 class_T *extends_cl)
409{
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200410 int member_count = objmembers_gap->ga_len;
411 if (member_count == 0)
412 return TRUE;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200413
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200414 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
415
416 // Validate each member variable
417 for (int c_i = 0; c_i < member_count; c_i++)
418 {
419 class_T *p_cl = extends_cl;
420 ocmember_T *c_m = members + c_i;
421 char_u *pstr = (*c_m->ocm_name == '_')
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200422 ? c_m->ocm_name + 1 : c_m->ocm_name;
423
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200424 // Check in all the parent classes in the lineage
425 while (p_cl != NULL)
426 {
427 int p_member_count = p_cl->class_obj_member_count;
428 if (p_member_count == 0)
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200429 {
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200430 p_cl = p_cl->class_extends;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200431 continue;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200432 }
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200433 ocmember_T *p_members = p_cl->class_obj_members;
434
435 // Compare against all the members in the parent class
436 for (int p_i = 0; p_i < p_member_count; p_i++)
437 {
438 ocmember_T *p_m = p_members + p_i;
439 char_u *qstr = (*p_m->ocm_name == '_')
440 ? p_m->ocm_name + 1 : p_m->ocm_name;
441 if (STRCMP(pstr, qstr) == 0)
442 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200443 semsg(_(e_duplicate_variable_str), c_m->ocm_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200444 return FALSE;
445 }
446 }
447
448 p_cl = p_cl->class_extends;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200449 }
450 }
451
452 return TRUE;
453}
454
455/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200456 * Compare the variable type of interface variables in "objmembers_gap" against
457 * the variable in any of the extended super interface lineage. Used to
458 * compare the variable types when extending interfaces. Returns TRUE if the
459 * variable types are the same.
460 */
461 static int
462extends_check_intf_var_type(
463 garray_T *objmembers_gap,
464 class_T *extends_cl)
465{
466 int member_count = objmembers_gap->ga_len;
467 if (member_count == 0)
468 return TRUE;
469
470 ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data);
471
472 // Validate each member variable
473 for (int c_i = 0; c_i < member_count; c_i++)
474 {
475 class_T *p_cl = extends_cl;
476 ocmember_T *c_m = members + c_i;
477 int var_found = FALSE;
478
479 // Check in all the parent classes in the lineage
480 while (p_cl != NULL && !var_found)
481 {
482 int p_member_count = p_cl->class_obj_member_count;
483 if (p_member_count == 0)
484 {
485 p_cl = p_cl->class_extends;
486 continue;
487 }
488 ocmember_T *p_members = p_cl->class_obj_members;
489
490 // Compare against all the members in the parent class
491 for (int p_i = 0; p_i < p_member_count; p_i++)
492 {
493 where_T where = WHERE_INIT;
494 ocmember_T *p_m = p_members + p_i;
495
496 if (STRCMP(p_m->ocm_name, c_m->ocm_name) != 0)
497 continue;
498
499 // Ensure the type is matching.
500 where.wt_func_name = (char *)c_m->ocm_name;
501 where.wt_kind = WT_MEMBER;
502
503 if (check_type(p_m->ocm_type, c_m->ocm_type, TRUE,
504 where) == FAIL)
505 return FALSE;
506
507 var_found = TRUE;
508 }
509
510 p_cl = p_cl->class_extends;
511 }
512 }
513
514 return TRUE;
515}
516
517/*
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200518 * When extending an abstract class, check whether all the abstract methods in
519 * the parent class are implemented. Returns TRUE if all the methods are
520 * implemented.
521 */
522 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200523validate_abstract_class_methods(
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200524 garray_T *classmethods_gap,
525 garray_T *objmethods_gap,
526 class_T *extends_cl)
527{
528 for (int loop = 1; loop <= 2; ++loop)
529 {
530 // loop == 1: check class methods
531 // loop == 2: check object methods
532 int extends_method_count = loop == 1
533 ? extends_cl->class_class_function_count
534 : extends_cl->class_obj_method_count;
535 if (extends_method_count == 0)
536 continue;
537
538 ufunc_T **extends_methods = loop == 1
539 ? extends_cl->class_class_functions
540 : extends_cl->class_obj_methods;
541
542 int method_count = loop == 1 ? classmethods_gap->ga_len
543 : objmethods_gap->ga_len;
544 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
545 ? classmethods_gap->ga_data
546 : objmethods_gap->ga_data);
547
548 for (int i = 0; i < extends_method_count; i++)
549 {
550 ufunc_T *uf = extends_methods[i];
Yegappan Lakshmanan1db15142023-09-19 20:34:05 +0200551 if (!IS_ABSTRACT_METHOD(uf))
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +0200552 continue;
553
554 int method_found = FALSE;
555
556 for (int j = 0; j < method_count; j++)
557 {
558 if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0)
559 {
560 method_found = TRUE;
561 break;
562 }
563 }
564
565 if (!method_found)
566 {
567 semsg(_(e_abstract_method_str_not_found), uf->uf_name);
568 return FALSE;
569 }
570 }
571 }
572
573 return TRUE;
574}
575
576/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200577 * Returns TRUE if the interface variable "if_var" is present in the list of
578 * variables in "cl_mt" or in the parent lineage of one of the extended classes
579 * in "extends_cl". For a class variable, 'is_class_var' is TRUE.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200580 */
581 static int
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200582intf_variable_present(
583 char_u *intf_class_name,
584 ocmember_T *if_var,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200585 ocmember_T *cl_mt,
586 int cl_member_count,
587 class_T *extends_cl)
588{
589 int variable_present = FALSE;
590
591 for (int cl_i = 0; cl_i < cl_member_count; ++cl_i)
592 {
593 ocmember_T *m = &cl_mt[cl_i];
594 where_T where = WHERE_INIT;
595
596 if (STRCMP(if_var->ocm_name, m->ocm_name) != 0)
597 continue;
598
599 // Ensure the access type is same
600 if (if_var->ocm_access != m->ocm_access)
601 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200602 semsg(_(e_variable_str_of_interface_str_has_different_access),
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200603 if_var->ocm_name, intf_class_name);
604 return FALSE;
605 }
606
607 // Ensure the type is matching.
608 if (m->ocm_type == &t_any)
609 {
610 // variable type is not specified. Use the variable type in the
611 // interface.
612 m->ocm_type = if_var->ocm_type;
613 }
614 else
615 {
616 where.wt_func_name = (char *)m->ocm_name;
617 where.wt_kind = WT_MEMBER;
618 if (check_type(if_var->ocm_type, m->ocm_type, TRUE,
619 where) == FAIL)
620 return FALSE;
621 }
622
623 variable_present = TRUE;
624 break;
625 }
626
627 if (!variable_present && extends_cl != NULL)
628 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200629 int ext_cl_count = extends_cl->class_obj_member_count;
630 ocmember_T *ext_cl_mt = extends_cl->class_obj_members;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200631 return intf_variable_present(intf_class_name, if_var,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200632 ext_cl_mt, ext_cl_count,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200633 extends_cl->class_extends);
634 }
635
636 return variable_present;
637}
638
639/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200640 * Check the variables of the interface class "ifcl" match object variables
641 * ("objmembers_gap") of a class.
642 * Returns TRUE if the object variables names are valid.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200643 */
644 static int
645validate_interface_variables(
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200646 char_u *intf_class_name,
647 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200648 garray_T *objmembers_gap,
649 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200650{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200651 int if_count = ifcl->class_obj_member_count;
652 if (if_count == 0)
653 return TRUE;
654
655 ocmember_T *if_ms = ifcl->class_obj_members;
656 ocmember_T *cl_ms = (ocmember_T *)(objmembers_gap->ga_data);
657 int cl_count = objmembers_gap->ga_len;
658 for (int if_i = 0; if_i < if_count; ++if_i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200659 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200660 if (!intf_variable_present(intf_class_name, &if_ms[if_i], cl_ms,
661 cl_count, extends_cl))
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200662 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200663 semsg(_(e_variable_str_of_interface_str_not_implemented),
664 if_ms[if_i].ocm_name, intf_class_name);
665 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200666 }
667 }
668
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200669 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200670}
671
672/*
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200673 * Returns TRUE if the method signature of "if_method" and "cl_method" matches.
674 */
675 static int
676intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method)
677{
678 where_T where = WHERE_INIT;
679
680 // Ensure the type is matching.
681 where.wt_func_name = (char *)if_method->uf_name;
682 where.wt_kind = WT_METHOD;
683 if (check_type(if_method->uf_func_type, cl_method->uf_func_type, TRUE,
684 where) == FAIL)
685 return FALSE;
686
687 return TRUE;
688}
689
690/*
691 * Returns TRUE if the interface method "if_ufunc" is present in the list of
692 * methods in "cl_fp" or in the parent lineage of one of the extended classes
693 * in "extends_cl". For a class method, 'is_class_method' is TRUE.
694 */
695 static int
696intf_method_present(
697 ufunc_T *if_ufunc,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200698 ufunc_T **cl_fp,
699 int cl_count,
700 class_T *extends_cl)
701{
702 int method_present = FALSE;
703
704 for (int cl_i = 0; cl_i < cl_count; ++cl_i)
705 {
706 char_u *cl_name = cl_fp[cl_i]->uf_name;
707 if (STRCMP(if_ufunc->uf_name, cl_name) == 0)
708 {
709 // Ensure the type is matching.
710 if (!intf_method_type_matches(if_ufunc, cl_fp[cl_i]))
711 return FALSE;
712 method_present = TRUE;
713 break;
714 }
715 }
716
717 if (!method_present && extends_cl != NULL)
718 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200719 ufunc_T **ext_cl_fp = (ufunc_T **)(extends_cl->class_obj_methods);
720 int ext_cl_count = extends_cl->class_obj_method_count;
721 return intf_method_present(if_ufunc, ext_cl_fp, ext_cl_count,
722 extends_cl->class_extends);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200723 }
724
725 return method_present;
726}
727
728/*
729 * Validate that a new class implements all the class/instance methods in the
730 * interface "ifcl". The new class methods are in "classfunctions_gap" and the
731 * new object methods are in "objmemthods_gap". Also validates the method
732 * types.
733 * Returns TRUE if all the interface class/object methods are implemented in
734 * the new class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200735 */
736 static int
737validate_interface_methods(
738 char_u *intf_class_name,
739 class_T *ifcl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200740 garray_T *objmethods_gap,
741 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200742{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200743 int if_count = ifcl->class_obj_method_count;
744 if (if_count == 0)
745 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200746
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200747 ufunc_T **if_fp = ifcl->class_obj_methods;
748 ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
749 int cl_count = objmethods_gap->ga_len;
750 for (int if_i = 0; if_i < if_count; ++if_i)
751 {
752 char_u *if_name = if_fp[if_i]->uf_name;
753
754 if (!intf_method_present(if_fp[if_i], cl_fp, cl_count, extends_cl))
755 {
756 semsg(_(e_method_str_of_interface_str_not_implemented),
757 if_name, intf_class_name);
758 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200759 }
760 }
761
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200762 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200763}
764
765/*
766 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200767 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200768 * object methods and object members in the new class are in
769 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
770 * "objmembers_gap" respectively.
771 */
772 static int
773validate_implements_classes(
774 garray_T *impl_gap,
775 class_T **intf_classes,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200776 garray_T *objmethods_gap,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200777 garray_T *objmembers_gap,
778 class_T *extends_cl)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200779{
780 int success = TRUE;
781
782 for (int i = 0; i < impl_gap->ga_len && success; ++i)
783 {
784 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
785 typval_T tv;
786 tv.v_type = VAR_UNKNOWN;
787 if (eval_variable_import(impl, &tv) == FAIL)
788 {
789 semsg(_(e_interface_name_not_found_str), impl);
790 success = FALSE;
791 break;
792 }
793
794 if (tv.v_type != VAR_CLASS
795 || tv.vval.v_class == NULL
796 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
797 {
798 semsg(_(e_not_valid_interface_str), impl);
799 success = FALSE;
800 clear_tv(&tv);
801 break;
802 }
803
804 class_T *ifcl = tv.vval.v_class;
805 intf_classes[i] = ifcl;
806 ++ifcl->class_refcount;
807
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +0200808 // check the variables of the interface match the members of the class
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200809 success = validate_interface_variables(impl, ifcl, objmembers_gap,
810 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200811
812 // check the functions/methods of the interface match the
813 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200814 if (success)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +0200815 success = validate_interface_methods(impl, ifcl, objmethods_gap,
816 extends_cl);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200817 clear_tv(&tv);
818 }
819
820 return success;
821}
822
823/*
824 * Check no function argument name is used as a class member.
825 * (Object members are always accessed with "this." prefix, so no need
826 * to check them.)
827 */
828 static int
829check_func_arg_names(
830 garray_T *classfunctions_gap,
831 garray_T *objmethods_gap,
832 garray_T *classmembers_gap)
833{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200834 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200835 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200836 {
837 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
838
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200839 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200840 {
841 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
842
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200843 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200844 {
845 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
846 garray_T *mgap = classmembers_gap;
847
848 // Check all the class member names
849 for (int mi = 0; mi < mgap->ga_len; ++mi)
850 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200851 char_u *mname =
852 ((ocmember_T *)mgap->ga_data + mi)->ocm_name;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200853 if (STRCMP(aname, mname) == 0)
854 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200855 if (uf->uf_script_ctx.sc_sid > 0)
856 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
857
858 semsg(_(e_argument_already_declared_in_class_str),
859 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200860
861 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200862 }
863 }
864 }
865 }
866 }
867
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200868 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200869}
870
871/*
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200872 * Returns TRUE if 'varname' is a reserved keyword name
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200873 */
874 static int
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200875is_reserved_varname(char_u *varname, char_u *varname_end)
876{
877 int reserved = FALSE;
878 char_u save_varname_end = *varname_end;
879 *varname_end = NUL;
880
881 reserved = check_reserved_name(varname, FALSE) == FAIL;
882
883 *varname_end = save_varname_end;
884
885 return reserved;
886}
887
888/*
889 * Returns TRUE if the variable "varname" is already defined either as a class
890 * variable or as an object variable.
891 */
892 static int
893is_duplicate_variable(
894 garray_T *class_members,
895 garray_T *obj_members,
896 char_u *varname,
897 char_u *varname_end)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200898{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200899 char_u *name = vim_strnsave(varname, varname_end - varname);
900 char_u *pstr = (*name == '_') ? name + 1 : name;
901 int dup = FALSE;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200902
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200903 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200904 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200905 // loop == 1: class variables, loop == 2: object variables
906 garray_T *vgap = (loop == 1) ? class_members : obj_members;
907 for (int i = 0; i < vgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200908 {
Yegappan Lakshmananf057aca2023-09-28 22:28:15 +0200909 ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i;
910 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1
911 : m->ocm_name;
912 if (STRCMP(pstr, qstr) == 0)
913 {
914 semsg(_(e_duplicate_variable_str), name);
915 dup = TRUE;
916 break;
917 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200918 }
919 }
920
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +0200921 vim_free(name);
922 return dup;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200923}
924
925/*
926 * Returns TRUE if the method "name" is already defined.
927 */
928 static int
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200929is_duplicate_method(
930 garray_T *classmethods_gap,
931 garray_T *objmethods_gap,
932 char_u *name)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200933{
934 char_u *pstr = (*name == '_') ? name + 1 : name;
935
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200936 // loop 1: class methods, loop 2: object methods
937 for (int loop = 1; loop <= 2; loop++)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200938 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200939 garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap;
940 for (int i = 0; i < fgap->ga_len; ++i)
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200941 {
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +0200942 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
943 char_u *qstr = *n == '_' ? n + 1 : n;
944 if (STRCMP(pstr, qstr) == 0)
945 {
946 semsg(_(e_duplicate_function_str), name);
947 return TRUE;
948 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200949 }
950 }
951
952 return FALSE;
953}
954
955/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200956 * Returns TRUE if the constructor is valid.
957 */
958 static int
959is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
960{
961 // Constructors are not allowed in abstract classes.
962 if (is_abstract)
963 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200964 emsg(_(e_cannot_define_new_method_in_abstract_class));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200965 return FALSE;
966 }
967 // A constructor is always static, no need to define it so.
968 if (has_static)
969 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200970 emsg(_(e_cannot_define_new_method_as_static));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200971 return FALSE;
972 }
973 // A return type should not be specified for the new()
974 // constructor method.
975 if (uf->uf_ret_type->tt_type != VAR_VOID)
976 {
RestorerZ7fe8f432023-09-24 23:21:24 +0200977 emsg(_(e_cannot_use_a_return_type_with_new_method));
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200978 return FALSE;
979 }
980 return TRUE;
981}
982
983/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +0100984 * Returns TRUE if 'uf' is a supported builtin method and has the correct
985 * method signature.
986 */
987 static int
988object_check_builtin_method_sig(ufunc_T *uf)
989{
990 char_u *name = uf->uf_name;
991 int valid = FALSE;
992 type_T method_sig;
993 type_T method_rt;
994 where_T where = WHERE_INIT;
995
996 // validate the method signature
997 CLEAR_FIELD(method_sig);
998 CLEAR_FIELD(method_rt);
999 method_sig.tt_type = VAR_FUNC;
1000
1001 if (STRCMP(name, "len") == 0)
1002 {
1003 // def __len(): number
1004 method_rt.tt_type = VAR_NUMBER;
1005 method_sig.tt_member = &method_rt;
1006 valid = TRUE;
1007 }
1008 else if (STRCMP(name, "empty") == 0)
1009 {
1010 // def __empty(): bool
1011 method_rt.tt_type = VAR_BOOL;
1012 method_sig.tt_member = &method_rt;
1013 valid = TRUE;
1014 }
1015 else if (STRCMP(name, "string") == 0)
1016 {
1017 // def __string(): string
1018 method_rt.tt_type = VAR_STRING;
1019 method_sig.tt_member = &method_rt;
1020 valid = TRUE;
1021 }
1022 else
1023 semsg(_(e_builtin_object_method_str_not_supported), uf->uf_name);
1024
1025 where.wt_func_name = (char *)uf->uf_name;
1026 where.wt_kind = WT_METHOD;
1027 if (valid && !check_type(&method_sig, uf->uf_func_type, TRUE, where))
1028 valid = FALSE;
1029
1030 return valid;
1031}
1032
1033/*
1034 * Returns TRUE if "funcname" is a supported builtin object method name
1035 */
1036 int
1037is_valid_builtin_obj_methodname(char_u *funcname)
1038{
1039 switch (funcname[0])
1040 {
1041 case 'e':
1042 return STRNCMP(funcname, "empty", 5) == 0;
1043
1044 case 'l':
1045 return STRNCMP(funcname, "len", 3) == 0;
1046
1047 case 'n':
1048 return STRNCMP(funcname, "new", 3) == 0;
1049
1050 case 's':
1051 return STRNCMP(funcname, "string", 6) == 0;
1052 }
1053
1054 return FALSE;
1055}
1056
1057
1058/*
1059 * Returns the builtin method "name" in object "obj". Returns NULL if the
1060 * method is not found.
1061 */
1062 ufunc_T *
1063class_get_builtin_method(
1064 class_T *cl,
1065 class_builtin_T builtin_method,
1066 int *method_idx)
1067{
1068 *method_idx = -1;
1069
1070 if (cl == NULL)
1071 return NULL;
1072
1073 *method_idx = cl->class_builtin_methods[builtin_method];
1074 return *method_idx != -1 ? cl->class_obj_methods[*method_idx] : NULL;
1075}
1076
1077/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001078 * Update the interface class lookup table for the member index on the
1079 * interface to the member index in the class implementing the interface.
1080 * And a lookup table for the object method index on the interface
1081 * to the object method index in the class implementing the interface.
1082 * This is also used for updating the lookup table for the extended class
1083 * hierarchy.
1084 */
1085 static int
1086update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001087 class_T *ifcl,
1088 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001089 garray_T *objmethods,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001090 int pobj_method_offset)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001091{
1092 if (ifcl == NULL)
1093 return OK;
1094
1095 // Table for members.
1096 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001097 + ifcl->class_obj_member_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001098 if (if2cl == NULL)
1099 return FAIL;
1100 if2cl->i2c_next = ifcl->class_itf2class;
1101 ifcl->class_itf2class = if2cl;
1102 if2cl->i2c_class = cl;
1103 if2cl->i2c_is_method = FALSE;
1104
1105 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
1106 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
1107 {
1108 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001109 cl->class_obj_members[cl_i].ocm_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001110 {
1111 int *table = (int *)(if2cl + 1);
1112 table[if_i] = cl_i;
1113 break;
1114 }
1115 }
1116
1117 // Table for methods.
1118 if2cl = alloc_clear(sizeof(itf2class_T)
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001119 + ifcl->class_obj_method_count * sizeof(int));
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001120 if (if2cl == NULL)
1121 return FAIL;
1122 if2cl->i2c_next = ifcl->class_itf2class;
1123 ifcl->class_itf2class = if2cl;
1124 if2cl->i2c_class = cl;
1125 if2cl->i2c_is_method = TRUE;
1126
1127 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
1128 {
1129 int done = FALSE;
1130 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
1131 {
1132 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001133 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001134 {
1135 int *table = (int *)(if2cl + 1);
1136 table[if_i] = cl_i;
1137 done = TRUE;
1138 break;
1139 }
1140 }
1141
1142 // extended class object method is not overridden by the child class.
1143 // Keep the method declared in one of the parent classes in the
1144 // lineage.
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001145 if (!done)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001146 {
1147 // If "ifcl" is not the immediate parent of "cl", then search in
1148 // the intermediate parent classes.
1149 if (cl->class_extends != ifcl)
1150 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001151 class_T *parent = cl->class_extends;
1152 int method_offset = objmethods->ga_len;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001153
1154 while (!done && parent != NULL && parent != ifcl)
1155 {
1156
1157 for (int cl_i = 0;
1158 cl_i < parent->class_obj_method_count_child; ++cl_i)
1159 {
1160 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
1161 parent->class_obj_methods[cl_i]->uf_name)
1162 == 0)
1163 {
1164 int *table = (int *)(if2cl + 1);
1165 table[if_i] = method_offset + cl_i;
1166 done = TRUE;
1167 break;
1168 }
1169 }
1170 method_offset += parent->class_obj_method_count_child;
1171 parent = parent->class_extends;
1172 }
1173 }
1174
1175 if (!done)
1176 {
1177 int *table = (int *)(if2cl + 1);
1178 table[if_i] = pobj_method_offset + if_i;
1179 }
1180 }
1181 }
1182
1183 return OK;
1184}
1185
1186/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001187 * Update the member and object method lookup tables for a new class in the
1188 * interface class.
1189 * For each interface add a lookup table for the member index on the interface
1190 * to the member index in the new class. And a lookup table for the object
1191 * method index on the interface to the object method index in the new class.
1192 */
1193 static int
1194add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
1195{
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001196 // update the lookup table for all the implemented interfaces
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001197 for (int i = 0; i < cl->class_interface_count; ++i)
1198 {
1199 class_T *ifcl = cl->class_interfaces_cl[i];
1200
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001201 // update the lookup table for this interface and all its super
1202 // interfaces.
1203 while (ifcl != NULL)
1204 {
1205 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
1206 0) == FAIL)
1207 return FAIL;
1208 ifcl = ifcl->class_extends;
1209 }
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001210 }
1211
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001212 // Update the lookup table for the extended class, if any
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001213 if (extends_cl != NULL)
1214 {
1215 class_T *pclass = extends_cl;
1216 int pobj_method_offset = objmethods_gap->ga_len;
1217
1218 // Update the entire lineage of extended classes.
1219 while (pclass != NULL)
1220 {
1221 if (update_member_method_lookup_table(pclass, cl,
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001222 objmethods_gap, pobj_method_offset) == FAIL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001223 return FAIL;
1224
1225 pobj_method_offset += pclass->class_obj_method_count_child;
1226 pclass = pclass->class_extends;
1227 }
1228 }
1229
1230 return OK;
1231}
1232
1233/*
1234 * Add class members to a new class. Allocate a typval for each class member
1235 * and initialize it.
1236 */
1237 static void
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001238add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001239{
1240 // Allocate a typval for each class member and initialize it.
1241 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
1242 cl->class_class_member_count);
1243 if (cl->class_members_tv == NULL)
1244 return;
1245
1246 for (int i = 0; i < cl->class_class_member_count; ++i)
1247 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001248 ocmember_T *m = &cl->class_class_members[i];
1249 typval_T *tv = &cl->class_members_tv[i];
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001250 if (m->ocm_init != NULL)
1251 {
1252 typval_T *etv = eval_expr(m->ocm_init, eap);
1253 if (etv != NULL)
1254 {
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001255 if (m->ocm_type->tt_type == VAR_ANY
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001256 && !(m->ocm_flags & OCMFLAG_HAS_TYPE)
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001257 && etv->v_type != VAR_SPECIAL)
1258 // If the member variable type is not yet set, then use
1259 // the initialization expression type.
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02001260 m->ocm_type = typval2type(etv, get_copyID(),
1261 type_list_gap,
1262 TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001263 *tv = *etv;
1264 vim_free(etv);
1265 }
1266 }
1267 else
1268 {
1269 // TODO: proper default value
1270 tv->v_type = m->ocm_type->tt_type;
1271 tv->vval.v_string = NULL;
1272 }
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001273 if (m->ocm_flags & OCMFLAG_CONST)
1274 item_lock(tv, DICT_MAXNEST, TRUE, TRUE);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001275 }
1276}
1277
1278/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001279 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001280 */
1281 static void
1282add_default_constructor(
1283 class_T *cl,
1284 garray_T *classfunctions_gap,
1285 garray_T *type_list_gap)
1286{
1287 garray_T fga;
1288
1289 ga_init2(&fga, 1, 1000);
1290 ga_concat(&fga, (char_u *)"new(");
1291 for (int i = 0; i < cl->class_obj_member_count; ++i)
1292 {
1293 if (i > 0)
1294 ga_concat(&fga, (char_u *)", ");
1295 ga_concat(&fga, (char_u *)"this.");
1296 ocmember_T *m = cl->class_obj_members + i;
1297 ga_concat(&fga, (char_u *)m->ocm_name);
1298 ga_concat(&fga, (char_u *)" = v:none");
1299 }
1300 ga_concat(&fga, (char_u *)")\nenddef\n");
1301 ga_append(&fga, NUL);
1302
1303 exarg_T fea;
1304 CLEAR_FIELD(fea);
1305 fea.cmdidx = CMD_def;
1306 fea.cmd = fea.arg = fga.ga_data;
1307
1308 garray_T lines_to_free;
1309 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1310
h-eastb895b0f2023-09-24 15:46:31 +02001311 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
1312 cl->class_obj_members, cl->class_obj_member_count);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001313
1314 ga_clear_strings(&lines_to_free);
1315 vim_free(fga.ga_data);
1316
1317 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
1318 {
1319 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001320 = nf;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001321 ++classfunctions_gap->ga_len;
1322
1323 nf->uf_flags |= FC_NEW;
1324 nf->uf_ret_type = get_type_ptr(type_list_gap);
1325 if (nf->uf_ret_type != NULL)
1326 {
1327 nf->uf_ret_type->tt_type = VAR_OBJECT;
1328 nf->uf_ret_type->tt_class = cl;
1329 nf->uf_ret_type->tt_argcount = 0;
1330 nf->uf_ret_type->tt_args = NULL;
1331 }
1332 }
1333}
1334
1335/*
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001336 * Add the class methods and object methods to the new class "cl".
1337 * When extending a class "extends_cl", add the instance methods from the
1338 * parent class also.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001339 */
1340 static int
1341add_classfuncs_objmethods(
1342 class_T *cl,
1343 class_T *extends_cl,
1344 garray_T *classfunctions_gap,
1345 garray_T *objmethods_gap)
1346{
1347 // loop 1: class functions, loop 2: object methods
1348 for (int loop = 1; loop <= 2; ++loop)
1349 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001350 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
1351 int *fcount = loop == 1 ? &cl->class_class_function_count
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001352 : &cl->class_obj_method_count;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001353 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001354 : &cl->class_obj_methods;
1355
1356 int parent_count = 0;
1357 if (extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001358 // Include object methods from the parent.
1359 // Don't include the parent class methods.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001360 parent_count = loop == 1
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001361 ? 0
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001362 : extends_cl->class_obj_method_count;
1363
1364 *fcount = parent_count + gap->ga_len;
1365 if (*fcount == 0)
1366 {
1367 *fup = NULL;
1368 continue;
1369 }
1370 *fup = ALLOC_MULT(ufunc_T *, *fcount);
1371 if (*fup == NULL)
1372 return FAIL;
1373
1374 if (gap->ga_len != 0)
1375 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
1376 vim_free(gap->ga_data);
1377 if (loop == 1)
1378 cl->class_class_function_count_child = gap->ga_len;
1379 else
1380 cl->class_obj_method_count_child = gap->ga_len;
1381
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001382 if (loop == 2)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001383 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001384 // Copy instance methods from the parent.
1385
1386 for (int i = 0; i < parent_count; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001387 {
Yegappan Lakshmanane2deb7e2023-09-16 18:05:07 +02001388 // Can't use the same parent function, because "uf_class" is
1389 // different and compilation will have a different result.
1390 // Put them after the functions in the current class, object
1391 // methods may be overruled, then "super.Method()" is used to
1392 // find a method from the parent.
1393 ufunc_T *pf = (extends_cl->class_obj_methods)[i];
1394 (*fup)[gap->ga_len + i] = copy_function(pf);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001395
1396 // If the child class overrides a function from the parent
1397 // the signature must be equal.
1398 char_u *pname = pf->uf_name;
1399 for (int ci = 0; ci < gap->ga_len; ++ci)
1400 {
1401 ufunc_T *cf = (*fup)[ci];
1402 char_u *cname = cf->uf_name;
1403 if (STRCMP(pname, cname) == 0)
1404 {
1405 where_T where = WHERE_INIT;
1406 where.wt_func_name = (char *)pname;
1407 where.wt_kind = WT_METHOD;
1408 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1409 TRUE, where);
1410 }
1411 }
1412 }
1413 }
1414
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001415 // Set the class pointer on all the functions and object methods.
1416 for (int i = 0; i < *fcount; ++i)
1417 {
1418 ufunc_T *fp = (*fup)[i];
1419 fp->uf_class = cl;
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001420 if (i < gap->ga_len)
1421 fp->uf_defclass = cl;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001422 if (loop == 2)
1423 fp->uf_flags |= FC_OBJECT;
1424 }
1425 }
1426
1427 return OK;
1428}
1429
1430/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001431 * Update the index of object methods called by builtin functions.
1432 */
1433 static void
1434update_builtin_method_index(class_T *cl)
1435{
1436 int i;
1437
1438 for (i = 0; i < CLASS_BUILTIN_MAX; i++)
1439 cl->class_builtin_methods[i] = -1;
1440
1441 for (i = 0; i < cl->class_obj_method_count; i++)
1442 {
1443 ufunc_T *uf = cl->class_obj_methods[i];
1444
1445 if (cl->class_builtin_methods[CLASS_BUILTIN_STRING] == -1
1446 && STRCMP(uf->uf_name, "string") == 0)
1447 cl->class_builtin_methods[CLASS_BUILTIN_STRING] = i;
1448 else if (cl->class_builtin_methods[CLASS_BUILTIN_EMPTY] == -1 &&
1449 STRCMP(uf->uf_name, "empty") == 0)
1450 cl->class_builtin_methods[CLASS_BUILTIN_EMPTY] = i;
1451 else if (cl->class_builtin_methods[CLASS_BUILTIN_LEN] == -1 &&
1452 STRCMP(uf->uf_name, "len") == 0)
1453 cl->class_builtin_methods[CLASS_BUILTIN_LEN] = i;
1454 }
1455}
1456
1457/*
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001458 * Return the end of the class name starting at "arg". Valid characters in a
1459 * class name are alphanumeric characters and "_". Also handles imported class
1460 * names.
1461 */
1462 static char_u *
1463find_class_name_end(char_u *arg)
1464{
1465 char_u *end = arg;
1466
1467 while (ASCII_ISALNUM(*end) || *end == '_'
1468 || (*end == '.' && (ASCII_ISALNUM(end[1]) || end[1] == '_')))
1469 ++end;
1470
1471 return end;
1472}
1473
1474
1475/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001476 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +00001477 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001478 */
1479 void
1480ex_class(exarg_T *eap)
1481{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001482 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
1483 long start_lnum = SOURCING_LNUM;
1484 char_u *arg = eap->arg;
1485 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001486
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001487 if (is_abstract)
1488 {
1489 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
1490 {
1491 semsg(_(e_invalid_argument_str), arg);
1492 return;
1493 }
1494 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001495 is_class = TRUE;
1496 }
1497
1498 if (!current_script_is_vim9()
1499 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001500 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001501 {
1502 if (is_class)
1503 emsg(_(e_class_can_only_be_defined_in_vim9_script));
1504 else
1505 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
1506 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001507 }
1508
1509 if (!ASCII_ISUPPER(*arg))
1510 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001511 if (is_class)
1512 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1513 else
1514 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1515 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001516 return;
1517 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001518 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1519 if (!IS_WHITE_OR_NUL(*name_end))
1520 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001521 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001522 return;
1523 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001524 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001525
Bram Moolenaara86655a2023-01-12 17:06:27 +00001526 // "export class" gets used when creating the class, don't use "is_export"
1527 // for the items inside the class.
1528 int class_export = is_export;
1529 is_export = FALSE;
1530
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001531 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001532 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001533
Bram Moolenaar83677162023-01-08 19:54:10 +00001534 // Name for "extends BaseClass"
1535 char_u *extends = NULL;
1536
Bram Moolenaar94674f22023-01-06 18:42:20 +00001537 // Names for "implements SomeInterface"
1538 garray_T ga_impl;
1539 ga_init2(&ga_impl, sizeof(char_u *), 5);
1540
1541 arg = skipwhite(name_end);
1542 while (*arg != NUL && *arg != '#' && *arg != '\n')
1543 {
1544 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001545 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001546 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1547 {
1548 if (extends != NULL)
1549 {
1550 emsg(_(e_duplicate_extends));
1551 goto early_ret;
1552 }
1553 arg = skipwhite(arg + 7);
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001554
1555 char_u *end = find_class_name_end(arg);
Bram Moolenaar83677162023-01-08 19:54:10 +00001556 if (!IS_WHITE_OR_NUL(*end))
1557 {
1558 semsg(_(e_white_space_required_after_name_str), arg);
1559 goto early_ret;
1560 }
1561 extends = vim_strnsave(arg, end - arg);
1562 if (extends == NULL)
1563 goto early_ret;
1564
1565 arg = skipwhite(end + 1);
1566 }
1567 else if (STRNCMP(arg, "implements", 10) == 0
1568 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001569 {
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001570 if (!is_class)
1571 {
1572 emsg(_(e_interface_cannot_use_implements));
1573 goto early_ret;
1574 }
1575
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001576 if (ga_impl.ga_len > 0)
1577 {
1578 emsg(_(e_duplicate_implements));
1579 goto early_ret;
1580 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001581 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001582
1583 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001584 {
Yegappan Lakshmanand2e1c832023-12-14 19:59:45 +01001585 char_u *impl_end = find_class_name_end(arg);
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001586 if ((!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1587 || (*impl_end == ','
1588 && !IS_WHITE_OR_NUL(*(impl_end + 1))))
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001589 {
1590 semsg(_(e_white_space_required_after_name_str), arg);
1591 goto early_ret;
1592 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001593 if (impl_end - arg == 0)
1594 {
1595 emsg(_(e_missing_name_after_implements));
1596 goto early_ret;
1597 }
1598
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001599 char_u *iname = vim_strnsave(arg, impl_end - arg);
1600 if (iname == NULL)
1601 goto early_ret;
1602 for (int i = 0; i < ga_impl.ga_len; ++i)
1603 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1604 {
1605 semsg(_(e_duplicate_interface_after_implements_str),
1606 iname);
1607 vim_free(iname);
1608 goto early_ret;
1609 }
1610 if (ga_add_string(&ga_impl, iname) == FAIL)
1611 {
1612 vim_free(iname);
1613 goto early_ret;
1614 }
1615 if (*impl_end != ',')
1616 {
1617 arg = skipwhite(impl_end);
1618 break;
1619 }
1620 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001621 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001622 }
1623 else
1624 {
1625 semsg(_(e_trailing_characters_str), arg);
1626early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001627 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001628 ga_clear_strings(&ga_impl);
1629 return;
1630 }
1631 }
1632
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001633 garray_T type_list; // list of pointers to allocated types
1634 ga_init2(&type_list, sizeof(type_T *), 10);
1635
Bram Moolenaard505d172022-12-18 21:42:55 +00001636 // Growarray with class members declared in the class.
1637 garray_T classmembers;
1638 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1639
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001640 // Growarray with functions declared in the class.
1641 garray_T classfunctions;
1642 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001643
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001644 // Growarray with object members declared in the class.
1645 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001646 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001647
1648 // Growarray with object methods declared in the class.
1649 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001650 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001651
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01001652 class_T *cl = NULL;
1653 class_T *extends_cl = NULL; // class from "extends" argument
1654 class_T **intf_classes = NULL;
1655
1656 cl = ALLOC_CLEAR_ONE(class_T);
1657 if (cl == NULL)
1658 goto cleanup;
1659 if (!is_class)
1660 cl->class_flags = CLASS_INTERFACE;
1661 else if (is_abstract)
1662 cl->class_flags = CLASS_ABSTRACT;
1663
1664 cl->class_refcount = 1;
1665 cl->class_name = vim_strnsave(name_start, name_end - name_start);
1666 if (cl->class_name == NULL)
1667 goto cleanup;
1668
1669 // Add the class to the script-local variables.
1670 // TODO: handle other context, e.g. in a function
1671 // TODO: does uf_hash need to be cleared?
1672 typval_T tv;
1673 tv.v_type = VAR_CLASS;
1674 tv.vval.v_class = cl;
1675 is_export = class_export;
1676 SOURCING_LNUM = start_lnum;
1677 int rc = set_var_const(cl->class_name, current_sctx.sc_sid,
1678 NULL, &tv, FALSE, 0, 0);
1679 if (rc == FAIL)
1680 goto cleanup;
1681
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001682 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001683 * Go over the body of the class/interface until "endclass" or
1684 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001685 */
1686 char_u *theline = NULL;
1687 int success = FALSE;
1688 for (;;)
1689 {
1690 vim_free(theline);
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001691 theline = eap->ea_getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001692 if (theline == NULL)
1693 break;
1694 char_u *line = skipwhite(theline);
1695
Bram Moolenaar418b5472022-12-20 13:38:22 +00001696 // Skip empty and comment lines.
1697 if (*line == NUL)
1698 continue;
1699 if (*line == '#')
1700 {
1701 if (vim9_bad_comment(line))
1702 break;
1703 continue;
1704 }
1705
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001706 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001707 char *end_name = is_class ? "endclass" : "endinterface";
1708 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001709 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001710 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001711 semsg(_(e_command_cannot_be_shortened_str), line);
1712 else if (*p == '|' || !ends_excmd2(line, p))
1713 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001714 else
1715 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001716 break;
1717 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001718 char *wrong_name = is_class ? "endinterface" : "endclass";
1719 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1720 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001721 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001722 break;
1723 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001724
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001725 int has_public = FALSE;
1726 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001727 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001728 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001729 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001730 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001731 break;
1732 }
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001733 if (!is_class)
1734 {
Yegappan Lakshmananb90e3bc2023-09-28 23:06:48 +02001735 emsg(_(e_public_variable_not_supported_in_interface));
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001736 break;
1737 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001738 has_public = TRUE;
1739 p = skipwhite(line + 6);
1740
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001741 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "static", 6) != 0
1742 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001743 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001744 emsg(_(e_public_must_be_followed_by_var_static_final_or_const));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001745 break;
1746 }
1747 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001748
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001749 int abstract_method = FALSE;
1750 char_u *pa = p;
1751 if (checkforcmd(&p, "abstract", 3))
1752 {
1753 if (STRNCMP(pa, "abstract", 8) != 0)
1754 {
1755 semsg(_(e_command_cannot_be_shortened_str), pa);
1756 break;
1757 }
1758
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01001759 if (!is_class)
1760 {
1761 // "abstract" not supported in an interface
1762 emsg(_(e_abstract_cannot_be_used_in_interface));
1763 break;
1764 }
1765
1766 if (!is_abstract)
1767 {
1768 semsg(_(e_abstract_method_in_concrete_class), pa);
1769 break;
1770 }
1771
Yegappan Lakshmanan5a539252023-11-04 09:42:46 +01001772 p = skipwhite(pa + 8);
1773 if (STRNCMP(p, "def", 3) != 0)
1774 {
1775 emsg(_(e_abstract_must_be_followed_by_def));
1776 break;
1777 }
1778
Yegappan Lakshmanan2b358ad2023-11-02 20:57:32 +01001779 abstract_method = TRUE;
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001780 }
1781
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001782 int has_static = FALSE;
1783 char_u *ps = p;
1784 if (checkforcmd(&p, "static", 4))
1785 {
1786 if (STRNCMP(ps, "static", 6) != 0)
1787 {
1788 semsg(_(e_command_cannot_be_shortened_str), ps);
1789 break;
1790 }
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001791
1792 if (!is_class)
1793 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001794 emsg(_(e_static_member_not_supported_in_interface));
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001795 break;
1796 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001797 has_static = TRUE;
1798 p = skipwhite(ps + 6);
Doug Kearns74da0ee2023-12-14 20:26:26 +01001799
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001800 if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "def", 3) != 0
1801 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
Doug Kearns74da0ee2023-12-14 20:26:26 +01001802 {
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001803 emsg(_(e_static_must_be_followed_by_var_def_final_or_const));
Doug Kearns74da0ee2023-12-14 20:26:26 +01001804 break;
1805 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001806 }
1807
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001808 int has_final = FALSE;
1809 int has_var = FALSE;
1810 int has_const = FALSE;
1811 if (checkforcmd(&p, "var", 3))
1812 has_var = TRUE;
1813 else if (checkforcmd(&p, "final", 5))
1814 {
1815 if (!is_class)
1816 {
1817 emsg(_(e_final_variable_not_supported_in_interface));
1818 break;
1819 }
1820 has_final = TRUE;
1821 }
1822 else if (checkforcmd(&p, "const", 5))
1823 {
1824 if (!is_class)
1825 {
1826 emsg(_(e_const_variable_not_supported_in_interface));
1827 break;
1828 }
1829 has_const = TRUE;
1830 }
1831 p = skipwhite(p);
1832
Bram Moolenaard505d172022-12-18 21:42:55 +00001833 // object members (public, read access, private):
Doug Kearns74da0ee2023-12-14 20:26:26 +01001834 // "var _varname"
1835 // "var varname"
1836 // "public var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001837 // "final _varname"
1838 // "final varname"
1839 // "public final varname"
1840 // "const _varname"
1841 // "const varname"
1842 // "public const varname"
Doug Kearns74da0ee2023-12-14 20:26:26 +01001843 // class members (public, read access, private):
1844 // "static var _varname"
1845 // "static var varname"
1846 // "public static var varname"
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001847 // "static final _varname"
1848 // "static final varname"
1849 // "public static final varname"
1850 // "static const _varname"
1851 // "static const varname"
1852 // "public static const varname"
1853 if (has_var || has_final || has_const)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001854 {
Doug Kearns74da0ee2023-12-14 20:26:26 +01001855 char_u *varname = p;
Bram Moolenaard505d172022-12-18 21:42:55 +00001856 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001857 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001858 char_u *init_expr = NULL;
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001859 int has_type = FALSE;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001860
Doug Kearns74da0ee2023-12-14 20:26:26 +01001861 if (!eval_isnamec1(*p))
1862 {
1863 if (has_static)
1864 semsg(_(e_invalid_class_variable_declaration_str), line);
1865 else
1866 semsg(_(e_invalid_object_variable_declaration_str), line);
1867 break;
1868 }
1869
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001870 if (!is_class && *varname == '_')
1871 {
1872 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01001873 semsg(_(e_protected_variable_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001874 varname);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001875 break;
1876 }
1877
Bram Moolenaard505d172022-12-18 21:42:55 +00001878 if (parse_member(eap, line, varname, has_public,
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02001879 &varname_end, &has_type, &type_list, &type,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001880 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001881 break;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001882
1883 if (is_reserved_varname(varname, varname_end)
1884 || is_duplicate_variable(&classmembers, &objmembers,
1885 varname, varname_end))
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001886 {
1887 vim_free(init_expr);
1888 break;
1889 }
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01001890 if (add_member(has_static ? &classmembers : &objmembers, varname,
1891 varname_end, has_public, has_final, has_const,
1892 has_type, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001893 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001894 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001895 break;
1896 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001897 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001898
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001899 // constructors:
1900 // def new()
1901 // enddef
1902 // def newOther()
1903 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001904 // object methods and class functions:
1905 // def SomeMethod()
1906 // enddef
1907 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001908 // enddef
1909 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001910 // def <Tval> someMethod()
1911 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001912 else if (checkforcmd(&p, "def", 3))
1913 {
1914 exarg_T ea;
1915 garray_T lines_to_free;
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001916 int is_new = STRNCMP(p, "new", 3) == 0;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001917
Yegappan Lakshmanan2dede3d2023-09-27 19:02:01 +02001918 if (has_public)
1919 {
1920 // "public" keyword is not supported when defining an object or
1921 // class method
1922 emsg(_(e_public_keyword_not_supported_for_method));
1923 break;
1924 }
1925
1926 if (*p == NUL)
1927 {
1928 // No method name following def
1929 semsg(_(e_not_valid_command_in_class_str), line);
1930 break;
1931 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001932
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001933 if (!is_class && *p == '_')
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01001934 {
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001935 // private methods are not supported in an interface
1936 semsg(_(e_protected_method_not_supported_in_interface), p);
1937 break;
1938 }
1939
1940 if (has_static && !is_new && SAFE_islower(*p) &&
1941 is_valid_builtin_obj_methodname(p))
1942 {
1943 semsg(_(e_builtin_class_method_not_supported), p);
Yegappan Lakshmananff6f0d52023-12-21 16:46:18 +01001944 break;
1945 }
1946
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001947 CLEAR_FIELD(ea);
1948 ea.cmd = line;
1949 ea.arg = p;
1950 ea.cmdidx = CMD_def;
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01001951 ea.ea_getline = eap->ea_getline;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001952 ea.cookie = eap->cookie;
1953
1954 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02001955 int class_flags;
1956 if (is_class)
1957 class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
1958 else
1959 class_flags = CF_INTERFACE;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001960 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
h-eastb895b0f2023-09-24 15:46:31 +02001961 class_flags, objmembers.ga_data, objmembers.ga_len);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001962 ga_clear_strings(&lines_to_free);
1963
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001964 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001965 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02001966 char_u *name = uf->uf_name;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001967
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001968 if (is_new && !is_valid_constructor(uf, is_abstract,
1969 has_static))
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001970 {
1971 // private variables are not supported in an interface
Ernie Rael03042a22023-11-11 08:53:32 +01001972 semsg(_(e_protected_method_not_supported_in_interface),
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02001973 name);
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02001974 func_clear_free(uf, FALSE);
1975 break;
1976 }
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01001977
1978 // check for builtin method
1979 if (!is_new && SAFE_islower(*name) &&
1980 !object_check_builtin_method_sig(uf))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001981 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001982 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001983 break;
1984 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001985
Bram Moolenaar58b40092023-01-11 15:59:05 +00001986 // Check the name isn't used already.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001987 if (is_duplicate_method(&classfunctions, &objmethods, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001988 {
1989 success = FALSE;
1990 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001991 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001992 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001993
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02001994 garray_T *fgap = has_static || is_new
1995 ? &classfunctions : &objmethods;
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001996 if (ga_grow(fgap, 1) == OK)
1997 {
1998 if (is_new)
1999 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002000
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002001 if (abstract_method)
2002 uf->uf_flags |= FC_ABSTRACT;
2003
Bram Moolenaar6acf7572023-01-01 19:53:30 +00002004 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
2005 ++fgap->ga_len;
2006 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002007 }
2008 }
2009
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002010 else
2011 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00002012 if (is_class)
2013 semsg(_(e_not_valid_command_in_class_str), line);
2014 else
2015 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002016 break;
2017 }
2018 }
2019 vim_free(theline);
2020
Bram Moolenaar83677162023-01-08 19:54:10 +00002021 /*
2022 * Check a few things before defining the class.
2023 */
2024
2025 // Check the "extends" class is valid.
2026 if (success && extends != NULL)
Yegappan Lakshmanan35b867b2024-03-09 15:44:19 +01002027 success = validate_extends_class(cl, extends, &extends_cl, is_class);
Bram Moolenaar83677162023-01-08 19:54:10 +00002028 VIM_CLEAR(extends);
2029
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002030 // Check the new object methods to make sure their access (public or
2031 // private) is the same as that in the extended class lineage.
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002032 if (success && extends_cl != NULL)
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002033 success = validate_extends_methods(&objmethods, extends_cl);
2034
2035 // Check the new class and object variables are not duplicates of the
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002036 // variables in the extended class lineage. If an interface is extending
2037 // another interface, then it can duplicate the member variables.
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002038 if (success && extends_cl != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02002039 {
2040 if (is_class)
2041 success = extends_check_dup_members(&objmembers, extends_cl);
2042 else
2043 success = extends_check_intf_var_type(&objmembers, extends_cl);
2044 }
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02002045
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002046 // When extending an abstract class, make sure all the abstract methods in
2047 // the parent class are implemented. If the current class is an abstract
2048 // class, then there is no need for this check.
2049 if (success && !is_abstract && extends_cl != NULL
2050 && (extends_cl->class_flags & CLASS_ABSTRACT))
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002051 success = validate_abstract_class_methods(&classfunctions,
2052 &objmethods, extends_cl);
Yegappan Lakshmanan7bcd25c2023-09-08 19:27:51 +02002053
Bram Moolenaar83677162023-01-08 19:54:10 +00002054 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00002055 if (success && ga_impl.ga_len > 0)
2056 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002057 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
2058
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002059 success = validate_implements_classes(&ga_impl, intf_classes,
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002060 &objmethods, &objmembers, extends_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00002061 }
2062
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002063 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00002064 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002065 success = check_func_arg_names(&classfunctions, &objmethods,
2066 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00002067
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002068 if (success)
2069 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002070 // "endclass" encountered without failures: Create the class.
2071
Bram Moolenaard0200c82023-01-28 15:19:40 +00002072 if (extends_cl != NULL)
2073 {
2074 cl->class_extends = extends_cl;
2075 extends_cl->class_flags |= CLASS_EXTENDED;
2076 }
Bram Moolenaar83677162023-01-08 19:54:10 +00002077
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002078 // Add class and object variables to "cl".
Bram Moolenaard505d172022-12-18 21:42:55 +00002079 if (add_members_to_class(&classmembers,
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002080 NULL,
2081 0,
Bram Moolenaar83677162023-01-08 19:54:10 +00002082 &cl->class_class_members,
2083 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00002084 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00002085 extends_cl == NULL ? NULL
2086 : extends_cl->class_obj_members,
2087 extends_cl == NULL ? 0
2088 : extends_cl->class_obj_member_count,
2089 &cl->class_obj_members,
2090 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00002091 goto cleanup;
2092
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00002093 if (ga_impl.ga_len > 0)
2094 {
2095 // Move the "implements" names into the class.
2096 cl->class_interface_count = ga_impl.ga_len;
2097 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
2098 if (cl->class_interfaces == NULL)
2099 goto cleanup;
2100 for (int i = 0; i < ga_impl.ga_len; ++i)
2101 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
2102 VIM_CLEAR(ga_impl.ga_data);
2103 ga_impl.ga_len = 0;
2104
Bram Moolenaard0200c82023-01-28 15:19:40 +00002105 cl->class_interfaces_cl = intf_classes;
2106 intf_classes = NULL;
2107 }
2108
2109 if (cl->class_interface_count > 0 || extends_cl != NULL)
2110 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002111 // Add a method and member lookup table to each of the interface
2112 // classes.
2113 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
2114 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00002115 }
2116
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002117 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00002118 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanand2f48002023-10-05 20:24:18 +02002119 add_class_members(cl, eap, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002120
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002121 int have_new = FALSE;
2122 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002123 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002124 {
2125 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
2126 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002127 {
2128 have_new = TRUE;
2129 break;
2130 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02002131 }
2132
2133 if (have_new)
2134 // The return type of new() is an object of class "cl"
2135 class_func->uf_ret_type->tt_class = cl;
2136 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002137 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002138 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002139
Bram Moolenaar58b40092023-01-11 15:59:05 +00002140 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02002141 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
2142 &objmethods) == FAIL)
2143 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002144
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01002145 update_builtin_method_index(cl);
2146
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002147 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00002148 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002149 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00002150 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002151 cl->class_type_list = type_list;
2152
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02002153 class_created(cl);
2154
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002155 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00002156 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002157
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002158 return;
2159 }
2160
2161cleanup:
Bram Moolenaar83677162023-01-08 19:54:10 +00002162 vim_free(extends);
2163 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00002164
2165 if (intf_classes != NULL)
2166 {
2167 for (int i = 0; i < ga_impl.ga_len; ++i)
2168 class_unref(intf_classes[i]);
2169 vim_free(intf_classes);
2170 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00002171 ga_clear_strings(&ga_impl);
2172
Bram Moolenaard505d172022-12-18 21:42:55 +00002173 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002174 {
Bram Moolenaard505d172022-12-18 21:42:55 +00002175 garray_T *gap = round == 1 ? &classmembers : &objmembers;
2176 if (gap->ga_len == 0 || gap->ga_data == NULL)
2177 continue;
2178
2179 for (int i = 0; i < gap->ga_len; ++i)
2180 {
2181 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
2182 vim_free(m->ocm_name);
2183 vim_free(m->ocm_init);
2184 }
2185 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002186 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002187
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002188 for (int i = 0; i < objmethods.ga_len; ++i)
2189 {
2190 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
2191 func_clear_free(uf, FALSE);
2192 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002193 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002194
2195 for (int i = 0; i < classfunctions.ga_len; ++i)
2196 {
2197 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
2198 func_clear_free(uf, FALSE);
2199 }
2200 ga_clear(&classfunctions);
2201
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002202 clear_type_list(&type_list);
2203}
2204
2205/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002206 * Find member "name" in class "cl", set "member_idx" to the member index and
2207 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002208 * When "is_object" is TRUE, then look for object members. Otherwise look for
2209 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002210 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02002211 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002212 */
2213 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002214oc_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002215 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02002216 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02002217 char_u *name,
2218 char_u *name_end,
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002219 int *member_idx)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002220{
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002221 size_t len = name_end - name;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002222 ocmember_T *m;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002223
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002224 *member_idx = -1; // not found (yet)
2225
2226 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
2227 member_idx);
2228 if (m == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002229 {
Yegappan Lakshmanan00cd1822023-09-18 19:56:49 +02002230 member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
2231 len);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002232 return &t_any;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002233 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00002234
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002235 return m->ocm_type;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002236}
2237
2238/*
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002239 * Given a class or object variable index, return the variable type
2240 */
2241 type_T *
Yegappan Lakshmanan1ea42882023-10-11 21:43:52 +02002242oc_member_type_by_idx(
Yegappan Lakshmananfe7b20a2023-10-04 19:47:52 +02002243 class_T *cl,
2244 int is_object,
2245 int member_idx)
2246{
2247 ocmember_T *m;
2248 int member_count;
2249
2250 if (is_object)
2251 {
2252 m = cl->class_obj_members;
2253 member_count = cl->class_obj_member_count;
2254 }
2255 else
2256 {
2257 m = cl->class_class_members;
2258 member_count = cl->class_class_member_count;
2259 }
2260
2261 if (member_idx >= member_count)
2262 return NULL;
2263
2264 return m[member_idx].ocm_type;
2265}
2266
2267/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002268 * Handle ":enum" up to ":endenum".
2269 */
2270 void
2271ex_enum(exarg_T *eap UNUSED)
2272{
2273 // TODO
2274}
2275
2276/*
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002277 * Type aliases (:type)
2278 */
2279
2280 void
2281typealias_free(typealias_T *ta)
2282{
2283 // ta->ta_type is freed in clear_type_list()
2284 vim_free(ta->ta_name);
2285 vim_free(ta);
2286}
2287
2288 void
2289typealias_unref(typealias_T *ta)
2290{
2291 if (ta != NULL && --ta->ta_refcount <= 0)
2292 typealias_free(ta);
2293}
2294
2295/*
2296 * Handle ":type". Create an alias for a type specification.
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002297 */
2298 void
2299ex_type(exarg_T *eap UNUSED)
2300{
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002301 char_u *arg = eap->arg;
2302
2303 if (!current_script_is_vim9()
2304 || (cmdmod.cmod_flags & CMOD_LEGACY)
Zoltan Arpadffy6fdb6282023-12-19 20:53:07 +01002305 || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002306 {
2307 emsg(_(e_type_can_only_be_defined_in_vim9_script));
2308 return;
2309 }
2310
2311 if (*arg == NUL)
2312 {
2313 emsg(_(e_missing_typealias_name));
2314 return;
2315 }
2316
2317 if (!ASCII_ISUPPER(*arg))
2318 {
2319 semsg(_(e_type_name_must_start_with_uppercase_letter_str), arg);
2320 return;
2321 }
2322
2323 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
2324 if (!IS_WHITE_OR_NUL(*name_end))
2325 {
2326 semsg(_(e_white_space_required_after_name_str), arg);
2327 return;
2328 }
2329 char_u *name_start = arg;
2330
2331 arg = skipwhite(name_end);
2332 if (*arg != '=')
2333 {
2334 semsg(_(e_missing_equal_str), arg);
2335 return;
2336 }
2337 if (!IS_WHITE_OR_NUL(*(arg + 1)))
2338 {
2339 semsg(_(e_white_space_required_after_str_str), "=", arg);
2340 return;
2341 }
2342 arg++;
2343 arg = skipwhite(arg);
2344
2345 if (*arg == NUL)
2346 {
2347 emsg(_(e_missing_typealias_type));
2348 return;
2349 }
2350
2351 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
2352 type_T *type = parse_type(&arg, &si->sn_type_list, TRUE);
2353 if (type == NULL)
2354 return;
2355
2356 if (*arg != NUL)
2357 {
2358 // some text after the type
2359 semsg(_(e_trailing_characters_str), arg);
2360 return;
2361 }
2362
2363 int cc = *name_end;
2364 *name_end = NUL;
2365
2366 typval_T tv;
2367 tv.v_type = VAR_UNKNOWN;
2368 if (eval_variable_import(name_start, &tv) == OK)
2369 {
2370 if (tv.v_type == VAR_TYPEALIAS)
2371 semsg(_(e_typealias_already_exists_for_str), name_start);
2372 else
2373 semsg(_(e_redefining_script_item_str), name_start);
2374 clear_tv(&tv);
2375 goto done;
2376 }
2377
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02002378 // Create a script-local variable for the type alias.
2379 if (type->tt_type != VAR_OBJECT)
2380 {
2381 tv.v_type = VAR_TYPEALIAS;
2382 tv.v_lock = 0;
2383 tv.vval.v_typealias = ALLOC_CLEAR_ONE(typealias_T);
2384 ++tv.vval.v_typealias->ta_refcount;
2385 tv.vval.v_typealias->ta_name = vim_strsave(name_start);
2386 tv.vval.v_typealias->ta_type = type;
2387 }
2388 else
2389 {
2390 // When creating a type alias for a class, use the class type itself to
2391 // create the type alias variable. This is needed to use the type
2392 // alias to invoke class methods (e.g. new()) and use class variables.
2393 tv.v_type = VAR_CLASS;
2394 tv.v_lock = 0;
2395 tv.vval.v_class = type->tt_class;
2396 ++tv.vval.v_class->class_refcount;
2397 }
Yegappan Lakshmananec3cebb2023-10-27 19:35:26 +02002398 set_var_const(name_start, current_sctx.sc_sid, NULL, &tv, FALSE,
2399 ASSIGN_CONST | ASSIGN_FINAL, 0);
2400
2401done:
2402 *name_end = cc;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002403}
2404
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002405/*
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002406 * Returns OK if a member variable named "name" is present in the class "cl".
2407 * Otherwise returns FAIL. If found, the member variable typval is set in
2408 * "rettv". If "is_object" is TRUE, then the object member variable table is
2409 * searched. Otherwise the class member variable table is searched.
2410 */
2411 static int
2412get_member_tv(
2413 class_T *cl,
2414 int is_object,
2415 char_u *name,
2416 size_t namelen,
2417 typval_T *rettv)
2418{
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002419 ocmember_T *m;
2420 int m_idx;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002421
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002422 m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
2423 &m_idx);
2424 if (m == NULL)
2425 return FAIL;
2426
2427 if (*name == '_')
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002428 {
Ernie Rael03042a22023-11-11 08:53:32 +01002429 emsg_var_cl_define(e_cannot_access_protected_variable_str,
Ernie Raele6c9aa52023-10-06 19:55:52 +02002430 m->ocm_name, 0, cl);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002431 return FAIL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002432 }
2433
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002434 if (is_object)
2435 {
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002436 // The object only contains a pointer to the class, the member values
2437 // array follows right after that.
2438 object_T *obj = rettv->vval.v_object;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002439 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2440 copy_tv(tv, rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002441 object_unref(obj);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002442 }
2443 else
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002444 {
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002445 copy_tv(&cl->class_members_tv[m_idx], rettv);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002446 class_unref(cl);
2447 }
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002448
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002449 return OK;
2450}
2451
2452/*
2453 * Call an object or class method "name" in class "cl". The method return
2454 * value is returned in "rettv".
2455 */
2456 static int
2457call_oc_method(
2458 class_T *cl,
2459 char_u *name,
2460 size_t len,
2461 char_u *name_end,
2462 evalarg_T *evalarg,
2463 char_u **arg,
2464 typval_T *rettv)
2465{
2466 ufunc_T *fp;
2467 typval_T argvars[MAX_FUNC_ARGS + 1];
2468 int argcount = 0;
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002469 ocmember_T *ocm = NULL;
2470 int m_idx;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002471
2472 fp = method_lookup(cl, rettv->v_type, name, len, NULL);
2473 if (fp == NULL)
2474 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002475 // could be an object or class funcref variable
2476 ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
2477 if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
2478 {
2479 method_not_found_msg(cl, rettv->v_type, name, len);
2480 return FAIL;
2481 }
2482
2483 if (rettv->v_type == VAR_OBJECT)
2484 {
2485 // funcref object variable
2486 object_T *obj = rettv->vval.v_object;
2487 typval_T *tv = (typval_T *)(obj + 1) + m_idx;
2488 copy_tv(tv, rettv);
2489 }
2490 else
2491 // funcref class variable
2492 copy_tv(&cl->class_members_tv[m_idx], rettv);
2493 *arg = name_end;
2494 return OK;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002495 }
2496
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002497 if (ocm == NULL && *fp->uf_name == '_')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002498 {
2499 // Cannot access a private method outside of a class
Ernie Rael03042a22023-11-11 08:53:32 +01002500 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002501 return FAIL;
2502 }
2503
2504 char_u *argp = name_end;
Ernie Raelb077b582023-12-14 20:11:44 +01002505 int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount, FALSE);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002506 if (ret == FAIL)
2507 return FAIL;
2508
2509 funcexe_T funcexe;
2510 CLEAR_FIELD(funcexe);
2511 funcexe.fe_evaluate = TRUE;
2512 if (rettv->v_type == VAR_OBJECT)
2513 {
2514 funcexe.fe_object = rettv->vval.v_object;
2515 ++funcexe.fe_object->obj_refcount;
2516 }
2517
2518 // Clear the class or object after calling the function, in
2519 // case the refcount is one.
2520 typval_T tv_tofree = *rettv;
2521 rettv->v_type = VAR_UNKNOWN;
2522
2523 // Call the user function. Result goes into rettv;
2524 int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
2525 NULL);
2526
2527 // Clear the previous rettv and the arguments.
2528 clear_tv(&tv_tofree);
2529 for (int idx = 0; idx < argcount; ++idx)
2530 clear_tv(&argvars[idx]);
2531
2532 if (error != FCERR_NONE)
2533 {
2534 user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
2535 return FAIL;
2536 }
2537 *arg = argp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002538
2539 return OK;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002540}
2541
2542/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002543 * Evaluate what comes after a class:
2544 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002545 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002546 * - class constructor: SomeClass.new()
2547 * - object member: someObject.varname
2548 * - object method: someObject.SomeMethod()
2549 *
2550 * "*arg" points to the '.'.
2551 * "*arg" is advanced to after the member name or method call.
2552 *
2553 * Returns FAIL or OK.
2554 */
2555 int
2556class_object_index(
2557 char_u **arg,
2558 typval_T *rettv,
2559 evalarg_T *evalarg,
2560 int verbose UNUSED) // give error messages
2561{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002562 if (VIM_ISWHITE((*arg)[1]))
2563 {
2564 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
2565 return FAIL;
2566 }
2567
2568 ++*arg;
2569 char_u *name = *arg;
2570 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2571 if (name_end == name)
2572 return FAIL;
2573 size_t len = name_end - name;
2574
Ernie Raeld615a312023-10-05 20:28:16 +02002575 int did_emsg_save = did_emsg;
Bram Moolenaar552bdca2023-02-17 21:08:50 +00002576 class_T *cl;
2577 if (rettv->v_type == VAR_CLASS)
2578 cl = rettv->vval.v_class;
2579 else // VAR_OBJECT
2580 {
2581 if (rettv->vval.v_object == NULL)
2582 {
2583 emsg(_(e_using_null_object));
2584 return FAIL;
2585 }
2586 cl = rettv->vval.v_object->obj_class;
2587 }
2588
Bram Moolenaard13dd302023-03-11 20:56:35 +00002589 if (cl == NULL)
2590 {
2591 emsg(_(e_incomplete_type));
2592 return FAIL;
2593 }
2594
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002595 if (*name_end == '(')
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002596 // Invoke the class or object method
2597 return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002598
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002599 else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002600 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002601 // Search in the object member variable table and the class member
2602 // variable table.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002603 int is_object = rettv->v_type == VAR_OBJECT;
2604 if (get_member_tv(cl, is_object, name, len, rettv) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002605 {
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002606 *arg = name_end;
2607 return OK;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002608 }
2609
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002610 // could be a class method or an object method
2611 int fidx;
2612 ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
2613 if (fp != NULL)
2614 {
2615 // Private methods are not accessible outside the class
2616 if (*name == '_')
2617 {
Ernie Rael03042a22023-11-11 08:53:32 +01002618 semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02002619 return FAIL;
2620 }
2621
2622 partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
2623 if (pt == NULL)
2624 return FAIL;
2625
2626 pt->pt_refcount = 1;
2627 if (is_object)
2628 {
2629 pt->pt_obj = rettv->vval.v_object;
2630 ++pt->pt_obj->obj_refcount;
2631 }
2632 pt->pt_auto = TRUE;
2633 pt->pt_func = fp;
2634 func_ptr_ref(pt->pt_func);
2635 rettv->v_type = VAR_PARTIAL;
2636 rettv->vval.v_partial = pt;
2637 *arg = name_end;
2638 return OK;
2639 }
2640
Ernie Raeld615a312023-10-05 20:28:16 +02002641 if (did_emsg == did_emsg_save)
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +02002642 member_not_found_msg(cl, rettv->v_type, name, len);
Bram Moolenaard505d172022-12-18 21:42:55 +00002643 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002644
2645 return FAIL;
2646}
2647
2648/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002649 * If "arg" points to a class or object method, return it.
2650 * Otherwise return NULL.
2651 */
2652 ufunc_T *
2653find_class_func(char_u **arg)
2654{
2655 char_u *name = *arg;
2656 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
2657 if (name_end == name || *name_end != '.')
2658 return NULL;
2659
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002660 ufunc_T *fp = NULL;
Yegappan Lakshmanan1689e842023-09-06 20:23:23 +02002661 size_t len = name_end - name;
2662 typval_T tv;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002663 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00002664 if (eval_variable(name, (int)len,
2665 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002666 return NULL;
2667 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00002668 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002669
2670 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
2671 : tv.vval.v_object->obj_class;
2672 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00002673 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002674 char_u *fname = name_end + 1;
2675 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
2676 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00002677 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002678 len = fname_end - fname;
2679
Ernie Rael4d00b832023-09-11 19:54:42 +02002680 fp = method_lookup(cl, tv.v_type, fname, len, NULL);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002681
Bram Moolenaareb533502022-12-14 15:06:11 +00002682fail_after_eval:
2683 clear_tv(&tv);
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002684 return fp;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00002685}
2686
2687/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02002688 * Returns the index of class variable "name" in the class "cl".
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002689 * Returns -1, if the variable is not found.
2690 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002691 */
2692 int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002693class_member_idx(class_T *cl, char_u *name, size_t namelen)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002694{
Ernie Rael4d00b832023-09-11 19:54:42 +02002695 int idx;
2696 class_member_lookup(cl, name, namelen, &idx);
2697 return idx;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00002698}
2699
2700/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002701 * Returns a pointer to the class member variable "name" in the class "cl".
2702 * Returns NULL if the variable is not found.
2703 * The member variable index is set in "idx".
2704 */
2705 ocmember_T *
2706class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2707{
Ernie Rael4d00b832023-09-11 19:54:42 +02002708 ocmember_T *ret_m = NULL;
2709 int ret_idx = -1;
2710 for (int i = 0; i < cl->class_class_member_count; ++i)
2711 {
2712 ocmember_T *m = &cl->class_class_members[i];
2713 if (namelen)
2714 {
2715 if (STRNCMP(name, m->ocm_name, namelen) == 0
2716 && m->ocm_name[namelen] == NUL)
2717 {
2718 ret_m = m;
2719 ret_idx = i;
2720 break;
2721 }
2722 }
2723 else if (STRCMP(name, m->ocm_name) == 0)
2724 {
2725 ret_m = m;
2726 ret_idx = i;
2727 break;
2728 }
2729 }
2730 if (idx != NULL)
2731 *idx = ret_idx;
2732 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002733}
2734
2735/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002736 * Returns a pointer to the class method "name" in class "cl".
2737 * Returns NULL if the method is not found.
2738 * The method index is set in "idx".
2739 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002740 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002741class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2742{
Ernie Rael4d00b832023-09-11 19:54:42 +02002743 ufunc_T *ret_fp = NULL;
2744 int ret_idx = -1;
2745 for (int i = 0; i < cl->class_class_function_count; ++i)
2746 {
2747 ufunc_T *fp = cl->class_class_functions[i];
2748 char_u *ufname = (char_u *)fp->uf_name;
2749 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2750 {
2751 ret_fp = fp;
2752 ret_idx = i;
2753 break;
2754 }
2755 }
2756 if (idx != NULL)
2757 *idx = ret_idx;
2758 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002759}
2760
2761/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002762 * Returns the index of class method "name" in the class "cl".
2763 * Returns -1, if the method is not found.
2764 */
2765 int
2766class_method_idx(class_T *cl, char_u *name, size_t namelen)
2767{
2768 int idx;
2769 class_method_lookup(cl, name, namelen, &idx);
2770 return idx;
2771}
2772
2773/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002774 * Returns the index of object member variable "name" in the class "cl".
2775 * Returns -1, if the variable is not found.
2776 * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
2777 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002778 static int
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002779object_member_idx(class_T *cl, char_u *name, size_t namelen)
2780{
Ernie Rael4d00b832023-09-11 19:54:42 +02002781 int idx;
2782 object_member_lookup(cl, name, namelen, &idx);
2783 return idx;
Yegappan Lakshmanan342f4f62023-09-09 11:37:23 +02002784}
2785
2786/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002787 * Returns a pointer to the object member variable "name" in the class "cl".
2788 * Returns NULL if the variable is not found.
2789 * The object member variable index is set in "idx".
2790 */
2791 ocmember_T *
2792object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2793{
Ernie Rael4d00b832023-09-11 19:54:42 +02002794 ocmember_T *ret_m = NULL;
2795 int ret_idx = -1;
2796 for (int i = 0; i < cl->class_obj_member_count; ++i)
2797 {
2798 ocmember_T *m = &cl->class_obj_members[i];
2799 if (namelen)
2800 {
2801 if (STRNCMP(name, m->ocm_name, namelen) == 0
2802 && m->ocm_name[namelen] == NUL)
2803 {
2804 ret_m = m;
2805 ret_idx = i;
2806 break;
2807 }
2808 }
2809 else if (STRCMP(name, m->ocm_name) == 0)
2810 {
2811 ret_m = m;
2812 ret_idx = i;
2813 break;
2814 }
2815 }
2816 if (idx != NULL)
2817 *idx = ret_idx;
2818 return ret_m;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002819}
2820
2821/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002822 * Returns a pointer to the object method "name" in class "cl".
2823 * Returns NULL if the method is not found.
2824 * The object method index is set in "idx".
2825 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002826 static ufunc_T *
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002827object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
2828{
Ernie Rael4d00b832023-09-11 19:54:42 +02002829 ufunc_T *ret_fp = NULL;
2830 int ret_idx = -1;
2831 for (int i = 0; i < cl->class_obj_method_count; ++i)
2832 {
2833 ufunc_T *fp = cl->class_obj_methods[i];
2834 // Use a separate pointer to avoid that ASAN complains about
2835 // uf_name[] only being 4 characters.
2836 char_u *ufname = (char_u *)fp->uf_name;
2837 if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
2838 {
2839 ret_fp = fp;
2840 ret_idx = i;
2841 break;
2842 }
2843 }
2844 if (idx != NULL)
2845 *idx = ret_idx;
2846 return ret_fp;
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002847}
2848
2849/*
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02002850 * Returns the index of object method "name" in the class "cl".
2851 * Returns -1, if the method is not found.
2852 */
2853 int
2854object_method_idx(class_T *cl, char_u *name, size_t namelen)
2855{
2856 int idx;
2857 object_method_lookup(cl, name, namelen, &idx);
2858 return idx;
2859}
2860
2861/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002862 * Lookup a class or object member variable by name. If v_type is VAR_CLASS,
2863 * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
2864 * object member variable.
2865 *
2866 * Returns a pointer to the member variable structure if variable is found.
2867 * Otherwise returns NULL. The member variable index is set in "*idx".
2868 */
2869 ocmember_T *
2870member_lookup(
2871 class_T *cl,
2872 vartype_T v_type,
2873 char_u *name,
2874 size_t namelen,
2875 int *idx)
2876{
2877 if (v_type == VAR_CLASS)
2878 return class_member_lookup(cl, name, namelen, idx);
2879 else
2880 return object_member_lookup(cl, name, namelen, idx);
2881}
2882
2883/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02002884 * Find the class that defines the named member. Look up the hierarchy
2885 * starting at "cl".
2886 *
2887 * Return the class that defines the member "name", else NULL.
2888 * Fill in "p_m", if specified, for ocmember_T in found class.
2889 */
2890// NOTE: if useful for something could also indirectly return vartype and idx.
2891 static class_T *
2892class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m)
2893{
2894 class_T *cl_found = NULL;
2895 vartype_T vartype = VAR_UNKNOWN;
2896 ocmember_T *m_found = NULL;
2897
2898 len = len != 0 ? len : STRLEN(name);
2899
2900 // Loop assumes if member is not defined in "cl", then it is not
2901 // defined in any super class; the last class where it's found is the
2902 // class where it is defined. Once the vartype is found, the other
2903 // type is no longer checked.
2904 for (class_T *super = cl; super != NULL; super = super->class_extends)
2905 {
2906 class_T *cl_tmp = NULL;
2907 ocmember_T *m = NULL;
2908 if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT)
2909 {
2910 if ((m = object_member_lookup(super, name, len, NULL)) != NULL)
2911 {
2912 cl_tmp = super;
2913 vartype = VAR_OBJECT;
2914 }
2915 }
2916 if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS)
2917 {
2918 if (( m = class_member_lookup(super, name, len, NULL)) != NULL)
2919 {
2920 cl_tmp = super;
2921 vartype = VAR_OBJECT;
2922 }
2923 }
2924 if (cl_tmp == NULL)
2925 break; // member is not in this or any super class.
2926 cl_found = cl_tmp;
2927 m_found = m;
2928 }
2929 if (p_m != NULL)
2930 *p_m = m_found;
2931 return cl_found;
2932}
2933
2934/*
Yegappan Lakshmananf36bbcd2023-09-10 18:19:06 +02002935 * Lookup a class or object method by name. If v_type is VAR_CLASS, then
2936 * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
2937 *
2938 * Returns a pointer to the method structure if variable is found.
2939 * Otherwise returns NULL. The method variable index is set in "*idx".
2940 */
2941 ufunc_T *
2942method_lookup(
2943 class_T *cl,
2944 vartype_T v_type,
2945 char_u *name,
2946 size_t namelen,
2947 int *idx)
2948{
2949 if (v_type == VAR_CLASS)
2950 return class_method_lookup(cl, name, namelen, idx);
2951 else
2952 return object_method_lookup(cl, name, namelen, idx);
2953}
2954
2955/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00002956 * Return TRUE if current context "cctx_arg" is inside class "cl".
2957 * Return FALSE if not.
2958 */
2959 int
2960inside_class(cctx_T *cctx_arg, class_T *cl)
2961{
2962 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
Ernie Raelcf138d42023-09-06 20:45:03 +02002963 if (cctx->ctx_ufunc != NULL
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02002964 && class_instance_of(cctx->ctx_ufunc->uf_class, cl))
Bram Moolenaar62a69232023-01-24 15:07:04 +00002965 return TRUE;
2966 return FALSE;
2967}
2968
2969/*
Yegappan Lakshmanane5437c52023-12-16 14:11:19 +01002970 * Return TRUE if object/class variable "m" is read-only.
2971 * Also give an error message.
2972 */
2973 int
2974oc_var_check_ro(class_T *cl, ocmember_T *m)
2975{
2976 if (m->ocm_flags & (OCMFLAG_FINAL | OCMFLAG_CONST))
2977 {
2978 semsg(_(e_cannot_change_readonly_variable_str_in_class_str),
2979 m->ocm_name, cl->class_name);
2980 return TRUE;
2981 }
2982 return FALSE;
2983}
2984
2985/*
2986 * Lock all the constant object variables. Called after creating and
2987 * initializing a new object.
2988 */
2989 void
2990obj_lock_const_vars(object_T *obj)
2991{
2992 for (int i = 0; i < obj->obj_class->class_obj_member_count; i++)
2993 {
2994 ocmember_T *ocm = &obj->obj_class->class_obj_members[i];
2995 if (ocm->ocm_flags & OCMFLAG_CONST)
2996 {
2997 typval_T *mtv = ((typval_T *)(obj + 1)) + i;
2998 item_lock(mtv, DICT_MAXNEST, TRUE, TRUE);
2999 }
3000 }
3001}
3002
3003/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003004 * Make a copy of an object.
3005 */
3006 void
3007copy_object(typval_T *from, typval_T *to)
3008{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003009 if (from->vval.v_object == NULL)
3010 to->vval.v_object = NULL;
3011 else
3012 {
3013 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003014 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003015 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003016}
3017
3018/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003019 * Make a copy of a class.
3020 */
3021 void
3022copy_class(typval_T *from, typval_T *to)
3023{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003024 if (from->vval.v_class == NULL)
3025 to->vval.v_class = NULL;
3026 else
3027 {
3028 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003029 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02003030 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003031}
3032
3033/*
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003034 * Free the class "cl" and its contents.
3035 */
3036 static void
3037class_free(class_T *cl)
3038{
3039 // Freeing what the class contains may recursively come back here.
3040 // Clear "class_name" first, if it is NULL the class does not need to
3041 // be freed.
3042 VIM_CLEAR(cl->class_name);
3043
3044 class_unref(cl->class_extends);
3045
3046 for (int i = 0; i < cl->class_interface_count; ++i)
3047 {
3048 vim_free(((char_u **)cl->class_interfaces)[i]);
3049 if (cl->class_interfaces_cl[i] != NULL)
3050 class_unref(cl->class_interfaces_cl[i]);
3051 }
3052 vim_free(cl->class_interfaces);
3053 vim_free(cl->class_interfaces_cl);
3054
3055 itf2class_T *next;
3056 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
3057 {
3058 next = i2c->i2c_next;
3059 vim_free(i2c);
3060 }
3061
3062 for (int i = 0; i < cl->class_class_member_count; ++i)
3063 {
3064 ocmember_T *m = &cl->class_class_members[i];
3065 vim_free(m->ocm_name);
3066 vim_free(m->ocm_init);
3067 if (cl->class_members_tv != NULL)
3068 clear_tv(&cl->class_members_tv[i]);
3069 }
3070 vim_free(cl->class_class_members);
3071 vim_free(cl->class_members_tv);
3072
3073 for (int i = 0; i < cl->class_obj_member_count; ++i)
3074 {
3075 ocmember_T *m = &cl->class_obj_members[i];
3076 vim_free(m->ocm_name);
3077 vim_free(m->ocm_init);
3078 }
3079 vim_free(cl->class_obj_members);
3080
3081 for (int i = 0; i < cl->class_class_function_count; ++i)
3082 {
3083 ufunc_T *uf = cl->class_class_functions[i];
3084 func_clear_free(uf, FALSE);
3085 }
3086 vim_free(cl->class_class_functions);
3087
3088 for (int i = 0; i < cl->class_obj_method_count; ++i)
3089 {
3090 ufunc_T *uf = cl->class_obj_methods[i];
3091 func_clear_free(uf, FALSE);
3092 }
3093 vim_free(cl->class_obj_methods);
3094
3095 clear_type_list(&cl->class_type_list);
3096
3097 class_cleared(cl);
3098
3099 vim_free(cl);
3100}
3101
3102/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003103 * Unreference a class. Free it when the reference count goes down to zero.
3104 */
3105 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003106class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003107{
Bram Moolenaard505d172022-12-18 21:42:55 +00003108 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003109 class_free(cl);
3110}
3111
3112/*
3113 * Go through the list of all classes and free items without "copyID".
3114 */
3115 int
3116class_free_nonref(int copyID)
3117{
3118 int did_free = FALSE;
3119
3120 for (class_T *cl = first_class; cl != NULL; cl = next_nonref_class)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003121 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003122 next_nonref_class = cl->class_next_used;
3123 if ((cl->class_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003124 {
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003125 // Free the class and items it contains.
3126 class_free(cl);
3127 did_free = TRUE;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00003128 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003129 }
Yegappan Lakshmanane651e112023-09-04 07:51:01 +02003130
3131 next_nonref_class = NULL;
3132 return did_free;
3133}
3134
3135 int
3136set_ref_in_classes(int copyID)
3137{
3138 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3139 set_ref_in_item_class(cl, copyID, NULL, NULL);
3140
3141 return FALSE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00003142}
3143
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003144static object_T *first_object = NULL;
3145
3146/*
3147 * Call this function when an object has been created. It will be added to the
3148 * list headed by "first_object".
3149 */
3150 void
3151object_created(object_T *obj)
3152{
3153 if (first_object != NULL)
3154 {
3155 obj->obj_next_used = first_object;
3156 first_object->obj_prev_used = obj;
3157 }
3158 first_object = obj;
3159}
3160
3161/*
3162 * Call this function when an object has been cleared and is about to be freed.
3163 * It is removed from the list headed by "first_object".
3164 */
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003165 static void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003166object_cleared(object_T *obj)
3167{
3168 if (obj->obj_next_used != NULL)
3169 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
3170 if (obj->obj_prev_used != NULL)
3171 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
3172 else if (first_object == obj)
3173 first_object = obj->obj_next_used;
3174}
3175
3176/*
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003177 * Free the contents of an object ignoring the reference count.
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003178 */
3179 static void
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003180object_free_contents(object_T *obj)
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003181{
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003182 class_T *cl = obj->obj_class;
3183
3184 if (!cl)
3185 return;
3186
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003187 // Avoid a recursive call, it can happen if "obj" has a circular reference.
3188 obj->obj_refcount = INT_MAX;
3189
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003190 // the member values are just after the object structure
3191 typval_T *tv = (typval_T *)(obj + 1);
3192 for (int i = 0; i < cl->class_obj_member_count; ++i)
3193 clear_tv(tv + i);
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003194}
3195
3196 static void
3197object_free_object(object_T *obj)
3198{
3199 class_T *cl = obj->obj_class;
3200
3201 if (!cl)
3202 return;
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003203
3204 // Remove from the list headed by "first_object".
3205 object_cleared(obj);
3206
3207 vim_free(obj);
3208 class_unref(cl);
3209}
3210
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003211 static void
3212object_free(object_T *obj)
3213{
3214 if (in_free_unref_items)
3215 return;
3216
3217 object_free_contents(obj);
3218 object_free_object(obj);
3219}
3220
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003221/*
3222 * Unreference an object.
3223 */
3224 void
3225object_unref(object_T *obj)
3226{
3227 if (obj != NULL && --obj->obj_refcount <= 0)
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003228 object_free(obj);
Yegappan Lakshmananb8523052023-10-08 19:07:39 +02003229}
3230
3231/*
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003232 * Go through the list of all objects and free items without "copyID".
3233 */
3234 int
3235object_free_nonref(int copyID)
3236{
3237 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003238
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003239 for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003240 {
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003241 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3242 {
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003243 // Free the object contents. Object itself will be freed later.
3244 object_free_contents(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00003245 did_free = TRUE;
3246 }
3247 }
3248
3249 return did_free;
3250}
3251
Yegappan Lakshmanan29bb67f2023-10-14 11:18:50 +02003252 void
3253object_free_items(int copyID)
3254{
3255 object_T *obj_next;
3256
3257 for (object_T *obj = first_object; obj != NULL; obj = obj_next)
3258 {
3259 obj_next = obj->obj_next_used;
3260 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
3261 object_free_object(obj);
3262 }
3263}
3264
LemonBoyafe04662023-08-23 21:08:11 +02003265/*
Ernie Raele6c9aa52023-10-06 19:55:52 +02003266 * Output message which takes a variable name and the class that defines it.
3267 * "cl" is that class where the name was found. Search "cl"'s hierarchy to
3268 * find the defining class.
3269 */
3270 void
3271emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl)
3272{
3273 ocmember_T *m;
3274 class_T *cl_def = class_defining_member(cl, name, len, &m);
3275 if (cl_def != NULL)
3276 semsg(_(msg), m->ocm_name, cl_def->class_name);
3277 else
3278 emsg(_(e_internal_error_please_report_a_bug));
3279}
3280
3281/*
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003282 * Echo a class or object method not found message.
3283 */
3284 void
3285method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3286{
3287 char_u *method_name = vim_strnsave(name, len);
3288 if ((v_type == VAR_OBJECT)
3289 && (class_method_idx(cl, name, len) >= 0))
3290 {
3291 // If this is a class method, then give a different error
3292 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003293 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003294 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003295 semsg(_(e_class_method_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003296 method_name, cl->class_name);
3297 }
3298 else if ((v_type == VAR_CLASS)
3299 && (object_method_idx(cl, name, len) >= 0))
3300 {
3301 // If this is an object method, then give a different error
3302 if (*name == '_')
Ernie Rael03042a22023-11-11 08:53:32 +01003303 semsg(_(e_cannot_access_protected_method_str), method_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003304 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003305 semsg(_(e_object_method_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003306 method_name, cl->class_name);
3307 }
3308 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003309 semsg(_(e_method_not_found_on_class_str_str), method_name,
3310 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003311 vim_free(method_name);
3312}
3313
3314/*
3315 * Echo a class or object member not found message.
3316 */
3317 void
3318member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
3319{
3320 char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name);
3321
3322 if (v_type == VAR_OBJECT)
3323 {
3324 if (class_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003325 semsg(_(e_class_variable_str_accessible_only_using_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003326 varname, cl->class_name);
3327 else
Ernie Raeld4802ec2023-10-20 11:59:00 +02003328 semsg(_(e_variable_not_found_on_object_str_str), varname,
3329 cl->class_name);
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003330 }
3331 else
3332 {
3333 if (object_member_idx(cl, name, len) >= 0)
RestorerZ7fe8f432023-09-24 23:21:24 +02003334 semsg(_(e_object_variable_str_accessible_only_using_object_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003335 varname, cl->class_name);
3336 else
RestorerZ7fe8f432023-09-24 23:21:24 +02003337 semsg(_(e_class_variable_str_not_found_in_class_str),
Yegappan Lakshmananc30a90d2023-09-15 20:14:55 +02003338 varname, cl->class_name);
3339 }
3340 vim_free(varname);
3341}
3342
3343/*
Yegappan Lakshmanan4f32c832024-01-12 17:36:40 +01003344 * Compile all the class and object methods in "cl".
3345 */
3346 void
3347defcompile_class(class_T *cl)
3348{
3349 for (int loop = 1; loop <= 2; ++loop)
3350 {
3351 int func_count = loop == 1 ? cl->class_class_function_count
3352 : cl->class_obj_method_count;
3353 for (int i = 0; i < func_count; i++)
3354 {
3355 ufunc_T *ufunc = loop == 1 ? cl->class_class_functions[i]
3356 : cl->class_obj_methods[i];
3357 defcompile_function(ufunc, cl);
3358 }
3359 }
3360}
3361
3362/*
3363 * Compile all the classes defined in the current script
3364 */
3365 void
3366defcompile_classes_in_script(void)
3367{
3368 for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
3369 {
3370 if (eval_variable(cl->class_name, 0, 0, NULL, NULL,
3371 EVAL_VAR_NOAUTOLOAD | EVAL_VAR_NO_FUNC) != FAIL)
3372 defcompile_class(cl);
3373 }
3374}
3375
3376/*
3377 * Returns TRUE if "name" is the name of a class. The typval for the class is
3378 * returned in "rettv".
3379 */
3380 int
3381is_class_name(char_u *name, typval_T *rettv)
3382{
3383 rettv->v_type = VAR_UNKNOWN;
3384
3385 if (eval_variable(name, 0, 0, rettv, NULL, EVAL_VAR_NOAUTOLOAD |
3386 EVAL_VAR_NO_FUNC) != FAIL)
3387 return rettv->v_type == VAR_CLASS;
3388 return FALSE;
3389}
3390
3391/*
Yegappan Lakshmanand3eae7b2024-03-03 16:26:58 +01003392 * Calls the object builtin method "name" with arguments "argv". The value
3393 * returned by the builtin method is in "rettv". Returns OK or FAIL.
3394 */
3395 static int
3396object_call_builtin_method(
3397 object_T *obj,
3398 class_builtin_T builtin_method,
3399 int argc,
3400 typval_T *argv,
3401 typval_T *rettv)
3402{
3403 ufunc_T *uf;
3404 int midx;
3405
3406 if (obj == NULL)
3407 return FAIL;
3408
3409 uf = class_get_builtin_method(obj->obj_class, builtin_method, &midx);
3410 if (uf == NULL)
3411 return FAIL;
3412
3413 funccall_T *fc = create_funccal(uf, rettv);
3414 int r;
3415
3416 if (fc == NULL)
3417 return FAIL;
3418
3419 ++obj->obj_refcount;
3420
3421 r = call_def_function(uf, argc, argv, 0, NULL, obj, fc, rettv);
3422
3423 remove_funccal();
3424
3425 return r;
3426}
3427
3428/*
3429 * Calls the object "empty()" method and returns the method retun value. In
3430 * case of an error, returns TRUE.
3431 */
3432 int
3433object_empty(object_T *obj)
3434{
3435 typval_T rettv;
3436
3437 if (object_call_builtin_method(obj, CLASS_BUILTIN_EMPTY, 0, NULL, &rettv)
3438 == FAIL)
3439 return TRUE;
3440
3441 return tv_get_bool(&rettv);
3442}
3443
3444/*
3445 * Use the object "len()" method to get an object length. Returns 0 if the
3446 * method is not found or there is an error.
3447 */
3448 int
3449object_len(object_T *obj)
3450{
3451 typval_T rettv;
3452
3453 if (object_call_builtin_method(obj, CLASS_BUILTIN_LEN, 0, NULL, &rettv)
3454 == FAIL)
3455 return 0;
3456
3457 return tv_to_number(&rettv);
3458}
3459
3460/*
3461 * Return a textual representation of object "obj"
3462 */
3463 char_u *
3464object_string(
3465 object_T *obj,
3466 char_u *numbuf,
3467 int copyID,
3468 int echo_style,
3469 int restore_copyID,
3470 int composite_val)
3471{
3472 typval_T rettv;
3473
3474 if (object_call_builtin_method(obj, CLASS_BUILTIN_STRING, 0, NULL, &rettv)
3475 == OK
3476 && rettv.vval.v_string != NULL)
3477 return rettv.vval.v_string;
3478 else
3479 {
3480 garray_T ga;
3481 ga_init2(&ga, 1, 50);
3482
3483 ga_concat(&ga, (char_u *)"object of ");
3484 class_T *cl = obj == NULL ? NULL : obj->obj_class;
3485 ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]"
3486 : cl->class_name);
3487 if (cl != NULL)
3488 {
3489 ga_concat(&ga, (char_u *)" {");
3490 for (int i = 0; i < cl->class_obj_member_count; ++i)
3491 {
3492 if (i > 0)
3493 ga_concat(&ga, (char_u *)", ");
3494 ocmember_T *m = &cl->class_obj_members[i];
3495 ga_concat(&ga, m->ocm_name);
3496 ga_concat(&ga, (char_u *)": ");
3497 char_u *tf = NULL;
3498 ga_concat(&ga, echo_string_core(
3499 (typval_T *)(obj + 1) + i,
3500 &tf, numbuf, copyID, echo_style,
3501 restore_copyID, composite_val));
3502 vim_free(tf);
3503 }
3504 ga_concat(&ga, (char_u *)"}");
3505 }
3506 return ga.ga_data;
3507 }
3508}
3509
3510/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02003511 * Return TRUE when the class "cl", its base class or one of the implemented
3512 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02003513 */
3514 int
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003515class_instance_of(class_T *cl, class_T *other_cl)
LemonBoyafe04662023-08-23 21:08:11 +02003516{
3517 if (cl == other_cl)
3518 return TRUE;
3519
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003520 // Recursively check the base classes.
3521 for (; cl != NULL; cl = cl->class_extends)
LemonBoyafe04662023-08-23 21:08:11 +02003522 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003523 if (cl == other_cl)
3524 return TRUE;
3525 // Check the implemented interfaces and the super interfaces
3526 for (int i = cl->class_interface_count - 1; i >= 0; --i)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003527 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003528 class_T *intf = cl->class_interfaces_cl[i];
3529 while (intf != NULL)
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003530 {
Yegappan Lakshmananb32064f2023-10-02 21:43:58 +02003531 if (intf == other_cl)
3532 return TRUE;
3533 // check the super interfaces
3534 intf = intf->class_extends;
Yegappan Lakshmanan92d9ee52023-09-17 17:03:19 +02003535 }
3536 }
LemonBoyafe04662023-08-23 21:08:11 +02003537 }
3538
3539 return FALSE;
3540}
3541
3542/*
Ernie Rael2025af12023-12-12 16:58:00 +01003543 * "instanceof(object, classinfo, ...)" function
LemonBoyafe04662023-08-23 21:08:11 +02003544 */
3545 void
3546f_instanceof(typval_T *argvars, typval_T *rettv)
3547{
3548 typval_T *object_tv = &argvars[0];
3549 typval_T *classinfo_tv = &argvars[1];
Yegappan Lakshmananfeaccd22023-10-28 15:53:55 +02003550 class_T *c;
LemonBoyafe04662023-08-23 21:08:11 +02003551
3552 rettv->vval.v_number = VVAL_FALSE;
3553
3554 if (check_for_object_arg(argvars, 0) == FAIL
Ernie Rael2025af12023-12-12 16:58:00 +01003555 || check_for_class_or_typealias_args(argvars, 1) == FAIL)
LemonBoyafe04662023-08-23 21:08:11 +02003556 return;
3557
Ernie Rael3da696d2023-09-19 20:14:18 +02003558 if (object_tv->vval.v_object == NULL)
3559 return;
3560
Ernie Rael2025af12023-12-12 16:58:00 +01003561 for (; classinfo_tv->v_type != VAR_UNKNOWN; ++classinfo_tv)
LemonBoyafe04662023-08-23 21:08:11 +02003562 {
Ernie Rael2025af12023-12-12 16:58:00 +01003563 if (classinfo_tv->v_type == VAR_TYPEALIAS)
3564 c = classinfo_tv->vval.v_typealias->ta_type->tt_class;
3565 else
3566 c = classinfo_tv->vval.v_class;
3567
3568 if (class_instance_of(object_tv->vval.v_object->obj_class, c))
LemonBoyafe04662023-08-23 21:08:11 +02003569 {
Ernie Rael2025af12023-12-12 16:58:00 +01003570 rettv->vval.v_number = VVAL_TRUE;
3571 return;
LemonBoyafe04662023-08-23 21:08:11 +02003572 }
3573 }
LemonBoyafe04662023-08-23 21:08:11 +02003574}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00003575
3576#endif // FEAT_EVAL