blob: 10b178ef1fe581fc5b83776c6917b7fef3124006 [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
24/*
Bram Moolenaard505d172022-12-18 21:42:55 +000025 * Parse a member declaration, both object and class member.
26 * Returns OK or FAIL. When OK then "varname_end" is set to just after the
27 * variable name and "type_ret" is set to the decleared or detected type.
28 * "init_expr" is set to the initialisation expression (allocated), if there is
Bram Moolenaar554d0312023-01-05 19:59:18 +000029 * one. For an interface "init_expr" is NULL.
Bram Moolenaard505d172022-12-18 21:42:55 +000030 */
31 static int
32parse_member(
33 exarg_T *eap,
34 char_u *line,
35 char_u *varname,
36 int has_public, // TRUE if "public" seen before "varname"
37 char_u **varname_end,
38 garray_T *type_list,
39 type_T **type_ret,
40 char_u **init_expr)
41{
42 *varname_end = to_name_end(varname, FALSE);
43 if (*varname == '_' && has_public)
44 {
45 semsg(_(e_public_member_name_cannot_start_with_underscore_str), line);
46 return FAIL;
47 }
48
49 char_u *colon = skipwhite(*varname_end);
50 char_u *type_arg = colon;
51 type_T *type = NULL;
52 if (*colon == ':')
53 {
54 if (VIM_ISWHITE(**varname_end))
55 {
56 semsg(_(e_no_white_space_allowed_before_colon_str), varname);
57 return FAIL;
58 }
59 if (!VIM_ISWHITE(colon[1]))
60 {
61 semsg(_(e_white_space_required_after_str_str), ":", varname);
62 return FAIL;
63 }
64 type_arg = skipwhite(colon + 1);
65 type = parse_type(&type_arg, type_list, TRUE);
66 if (type == NULL)
67 return FAIL;
68 }
69
70 char_u *expr_start = skipwhite(type_arg);
71 char_u *expr_end = expr_start;
72 if (type == NULL && *expr_start != '=')
73 {
74 emsg(_(e_type_or_initialization_required));
75 return FAIL;
76 }
77
78 if (*expr_start == '=')
79 {
80 if (!VIM_ISWHITE(expr_start[-1]) || !VIM_ISWHITE(expr_start[1]))
81 {
82 semsg(_(e_white_space_required_before_and_after_str_at_str),
83 "=", type_arg);
84 return FAIL;
85 }
86 expr_start = skipwhite(expr_start + 1);
87
88 expr_end = expr_start;
89 evalarg_T evalarg;
90 fill_evalarg_from_eap(&evalarg, eap, FALSE);
91 skip_expr(&expr_end, NULL);
92
93 if (type == NULL)
94 {
95 // No type specified, use the type of the initializer.
96 typval_T tv;
97 tv.v_type = VAR_UNKNOWN;
98 char_u *expr = expr_start;
99 int res = eval0(expr, &tv, eap, &evalarg);
100
101 if (res == OK)
Bram Moolenaare83c1332023-01-02 21:04:04 +0000102 {
Bram Moolenaard505d172022-12-18 21:42:55 +0000103 type = typval2type(&tv, get_copyID(), type_list,
104 TVTT_DO_MEMBER);
Bram Moolenaare83c1332023-01-02 21:04:04 +0000105 clear_tv(&tv);
106 }
Bram Moolenaard505d172022-12-18 21:42:55 +0000107 if (type == NULL)
108 {
109 semsg(_(e_cannot_get_object_member_type_from_initializer_str),
110 expr_start);
111 clear_evalarg(&evalarg, NULL);
112 return FAIL;
113 }
114 }
115 clear_evalarg(&evalarg, NULL);
116 }
117 if (!valid_declaration_type(type))
118 return FAIL;
119
120 *type_ret = type;
121 if (expr_end > expr_start)
Bram Moolenaar554d0312023-01-05 19:59:18 +0000122 {
123 if (init_expr == NULL)
124 {
125 emsg(_(e_cannot_initialize_member_in_interface));
126 return FAIL;
127 }
Bram Moolenaard505d172022-12-18 21:42:55 +0000128 *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
Bram Moolenaar554d0312023-01-05 19:59:18 +0000129 }
Bram Moolenaard505d172022-12-18 21:42:55 +0000130 return OK;
131}
132
133/*
134 * Add a member to an object or a class.
135 * Returns OK when successful, "init_expr" will be consumed then.
136 * Returns FAIL otherwise, caller might need to free "init_expr".
137 */
138 static int
139add_member(
140 garray_T *gap,
141 char_u *varname,
142 char_u *varname_end,
143 int has_public,
144 type_T *type,
145 char_u *init_expr)
146{
147 if (ga_grow(gap, 1) == FAIL)
148 return FAIL;
149 ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
150 m->ocm_name = vim_strnsave(varname, varname_end - varname);
=?UTF-8?q?Ola=20S=C3=B6der?=d8742472023-03-05 13:12:32 +0000151 m->ocm_access = has_public ? VIM_ACCESS_ALL
152 : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
Bram Moolenaard505d172022-12-18 21:42:55 +0000153 m->ocm_type = type;
154 if (init_expr != NULL)
155 m->ocm_init = init_expr;
156 ++gap->ga_len;
157 return OK;
158}
159
160/*
161 * Move the class or object members found while parsing a class into the class.
162 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000163 * "parent_members" points to the members in the parent class (if any)
164 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000165 * "members" will be set to the newly allocated array of members and
166 * "member_count" set to the number of members.
167 * Returns OK or FAIL.
168 */
169 static int
170add_members_to_class(
171 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000172 ocmember_T *parent_members,
173 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000174 ocmember_T **members,
175 int *member_count)
176{
Bram Moolenaar83677162023-01-08 19:54:10 +0000177 *member_count = parent_count + gap->ga_len;
178 *members = *member_count == 0 ? NULL
179 : ALLOC_MULT(ocmember_T, *member_count);
180 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000181 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000182 for (int i = 0; i < parent_count; ++i)
183 {
184 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000185 ocmember_T *m = *members + i;
186 *m = parent_members[i];
187 m->ocm_name = vim_strsave(m->ocm_name);
188 if (m->ocm_init != NULL)
189 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000190 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000191 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000192 // new members are moved
193 mch_memmove(*members + parent_count,
194 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000195 VIM_CLEAR(gap->ga_data);
196 return OK;
197}
198
199/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000200 * Convert a member index "idx" of interface "itf" to the member index of class
201 * "cl" implementing that interface.
202 */
203 int
Bram Moolenaard0200c82023-01-28 15:19:40 +0000204object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000205{
Bram Moolenaard0200c82023-01-28 15:19:40 +0000206 if (idx > (is_method ? itf->class_obj_method_count
207 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000208 {
209 siemsg("index %d out of range for interface %s", idx, itf->class_name);
210 return 0;
211 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200212
213 // If "cl" is the interface or the class that is extended, then the method
214 // index can be used directly and there is no need to search for the method
215 // index in one of the child classes.
216 if (cl == itf)
217 return idx;
218
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000219 itf2class_T *i2c;
220 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Bram Moolenaard0200c82023-01-28 15:19:40 +0000221 if (i2c->i2c_class == cl && i2c->i2c_is_method == is_method)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000222 break;
223 if (i2c == NULL)
224 {
225 siemsg("class %s not found on interface %s",
226 cl->class_name, itf->class_name);
227 return 0;
228 }
229 int *table = (int *)(i2c + 1);
230 return table[idx];
231}
232
233/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200234 * Update the interface class lookup table for the member index on the
235 * interface to the member index in the class implementing the interface.
236 * And a lookup table for the object method index on the interface
237 * to the object method index in the class implementing the interface.
238 * This is also used for updating the lookup table for the extended class
239 * hierarchy.
240 */
241 static int
242update_member_method_lookup_table(
243 class_T *ifcl,
244 class_T *cl,
245 garray_T *objmethods,
246 int pobj_method_offset,
247 int is_interface)
248{
249 if (ifcl == NULL)
250 return OK;
251
252 // Table for members.
253 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
254 + ifcl->class_obj_member_count * sizeof(int));
255 if (if2cl == NULL)
256 return FAIL;
257 if2cl->i2c_next = ifcl->class_itf2class;
258 ifcl->class_itf2class = if2cl;
259 if2cl->i2c_class = cl;
260 if2cl->i2c_is_method = FALSE;
261
262 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
263 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
264 {
265 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
266 cl->class_obj_members[cl_i].ocm_name) == 0)
267 {
268 int *table = (int *)(if2cl + 1);
269 table[if_i] = cl_i;
270 break;
271 }
272 }
273
274 // Table for methods.
275 if2cl = alloc_clear(sizeof(itf2class_T)
276 + ifcl->class_obj_method_count * sizeof(int));
277 if (if2cl == NULL)
278 return FAIL;
279 if2cl->i2c_next = ifcl->class_itf2class;
280 ifcl->class_itf2class = if2cl;
281 if2cl->i2c_class = cl;
282 if2cl->i2c_is_method = TRUE;
283
284 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
285 {
286 int done = FALSE;
287 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
288 {
289 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
290 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name)
291 == 0)
292 {
293 int *table = (int *)(if2cl + 1);
294 table[if_i] = cl_i;
295 done = TRUE;
296 break;
297 }
298 }
299
300 // extended class object method is not overridden by the child class.
301 // Keep the method declared in one of the parent classes in the
302 // lineage.
303 if (!done && !is_interface)
304 {
305 // If "ifcl" is not the immediate parent of "cl", then search in
306 // the intermediate parent classes.
307 if (cl->class_extends != ifcl)
308 {
309 class_T *parent = cl->class_extends;
310 int method_offset = objmethods->ga_len;
311
312 while (!done && parent != NULL && parent != ifcl)
313 {
314
315 for (int cl_i = 0;
316 cl_i < parent->class_obj_method_count_child; ++cl_i)
317 {
318 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
319 parent->class_obj_methods[cl_i]->uf_name)
320 == 0)
321 {
322 int *table = (int *)(if2cl + 1);
323 table[if_i] = method_offset + cl_i;
324 done = TRUE;
325 break;
326 }
327 }
328 method_offset += parent->class_obj_method_count_child;
329 parent = parent->class_extends;
330 }
331 }
332
333 if (!done)
334 {
335 int *table = (int *)(if2cl + 1);
336 table[if_i] = pobj_method_offset + if_i;
337 }
338 }
339 }
340
341 return OK;
342}
343
344/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000345 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +0000346 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000347 */
348 void
349ex_class(exarg_T *eap)
350{
Bram Moolenaar83ae6152023-02-25 19:59:31 +0000351 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
352 long start_lnum = SOURCING_LNUM;
Bram Moolenaar554d0312023-01-05 19:59:18 +0000353
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000354 char_u *arg = eap->arg;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000355 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000356 if (is_abstract)
357 {
358 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
359 {
360 semsg(_(e_invalid_argument_str), arg);
361 return;
362 }
363 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +0000364 is_class = TRUE;
365 }
366
367 if (!current_script_is_vim9()
368 || (cmdmod.cmod_flags & CMOD_LEGACY)
369 || !getline_equal(eap->getline, eap->cookie, getsourceline))
370 {
371 if (is_class)
372 emsg(_(e_class_can_only_be_defined_in_vim9_script));
373 else
374 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
375 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000376 }
377
378 if (!ASCII_ISUPPER(*arg))
379 {
Bram Moolenaar554d0312023-01-05 19:59:18 +0000380 if (is_class)
381 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
382 else
383 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
384 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000385 return;
386 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000387 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
388 if (!IS_WHITE_OR_NUL(*name_end))
389 {
Bram Moolenaar554d0312023-01-05 19:59:18 +0000390 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000391 return;
392 }
Bram Moolenaar94674f22023-01-06 18:42:20 +0000393 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000394
Bram Moolenaara86655a2023-01-12 17:06:27 +0000395 // "export class" gets used when creating the class, don't use "is_export"
396 // for the items inside the class.
397 int class_export = is_export;
398 is_export = FALSE;
399
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000400 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000401 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000402
Bram Moolenaar83677162023-01-08 19:54:10 +0000403 // Name for "extends BaseClass"
404 char_u *extends = NULL;
405
Bram Moolenaar94674f22023-01-06 18:42:20 +0000406 // Names for "implements SomeInterface"
407 garray_T ga_impl;
408 ga_init2(&ga_impl, sizeof(char_u *), 5);
409
410 arg = skipwhite(name_end);
411 while (*arg != NUL && *arg != '#' && *arg != '\n')
412 {
413 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +0000414 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +0000415 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
416 {
417 if (extends != NULL)
418 {
419 emsg(_(e_duplicate_extends));
420 goto early_ret;
421 }
422 arg = skipwhite(arg + 7);
423 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
424 if (!IS_WHITE_OR_NUL(*end))
425 {
426 semsg(_(e_white_space_required_after_name_str), arg);
427 goto early_ret;
428 }
429 extends = vim_strnsave(arg, end - arg);
430 if (extends == NULL)
431 goto early_ret;
432
433 arg = skipwhite(end + 1);
434 }
435 else if (STRNCMP(arg, "implements", 10) == 0
436 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +0000437 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +0000438 if (ga_impl.ga_len > 0)
439 {
440 emsg(_(e_duplicate_implements));
441 goto early_ret;
442 }
Bram Moolenaar94674f22023-01-06 18:42:20 +0000443 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +0000444
445 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +0000446 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +0000447 char_u *impl_end = find_name_end(arg, NULL, NULL,
448 FNE_CHECK_START);
449 if (!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
450 {
451 semsg(_(e_white_space_required_after_name_str), arg);
452 goto early_ret;
453 }
454 char_u *iname = vim_strnsave(arg, impl_end - arg);
455 if (iname == NULL)
456 goto early_ret;
457 for (int i = 0; i < ga_impl.ga_len; ++i)
458 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
459 {
460 semsg(_(e_duplicate_interface_after_implements_str),
461 iname);
462 vim_free(iname);
463 goto early_ret;
464 }
465 if (ga_add_string(&ga_impl, iname) == FAIL)
466 {
467 vim_free(iname);
468 goto early_ret;
469 }
470 if (*impl_end != ',')
471 {
472 arg = skipwhite(impl_end);
473 break;
474 }
475 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +0000476 }
Bram Moolenaar94674f22023-01-06 18:42:20 +0000477 }
478 else
479 {
480 semsg(_(e_trailing_characters_str), arg);
481early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +0000482 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +0000483 ga_clear_strings(&ga_impl);
484 return;
485 }
486 }
487
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000488 garray_T type_list; // list of pointers to allocated types
489 ga_init2(&type_list, sizeof(type_T *), 10);
490
Bram Moolenaard505d172022-12-18 21:42:55 +0000491 // Growarray with class members declared in the class.
492 garray_T classmembers;
493 ga_init2(&classmembers, sizeof(ocmember_T), 10);
494
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000495 // Growarray with functions declared in the class.
496 garray_T classfunctions;
497 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +0000498
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000499 // Growarray with object members declared in the class.
500 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +0000501 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000502
503 // Growarray with object methods declared in the class.
504 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000505 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000506
507 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +0000508 * Go over the body of the class/interface until "endclass" or
509 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000510 */
511 char_u *theline = NULL;
512 int success = FALSE;
513 for (;;)
514 {
515 vim_free(theline);
516 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
517 if (theline == NULL)
518 break;
519 char_u *line = skipwhite(theline);
520
Bram Moolenaar418b5472022-12-20 13:38:22 +0000521 // Skip empty and comment lines.
522 if (*line == NUL)
523 continue;
524 if (*line == '#')
525 {
526 if (vim9_bad_comment(line))
527 break;
528 continue;
529 }
530
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000531 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +0000532 char *end_name = is_class ? "endclass" : "endinterface";
533 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000534 {
Bram Moolenaar554d0312023-01-05 19:59:18 +0000535 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000536 semsg(_(e_command_cannot_be_shortened_str), line);
537 else if (*p == '|' || !ends_excmd2(line, p))
538 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +0000539 else
540 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000541 break;
542 }
Bram Moolenaar554d0312023-01-05 19:59:18 +0000543 char *wrong_name = is_class ? "endinterface" : "endclass";
544 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
545 {
Bram Moolenaar657aea72023-01-27 13:16:19 +0000546 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +0000547 break;
548 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000549
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000550 int has_public = FALSE;
551 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000552 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000553 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000554 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000555 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000556 break;
557 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000558 has_public = TRUE;
559 p = skipwhite(line + 6);
560
Bram Moolenaard505d172022-12-18 21:42:55 +0000561 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000562 {
Bram Moolenaard505d172022-12-18 21:42:55 +0000563 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000564 break;
565 }
566 }
Bram Moolenaard505d172022-12-18 21:42:55 +0000567
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000568 int has_static = FALSE;
569 char_u *ps = p;
570 if (checkforcmd(&p, "static", 4))
571 {
572 if (STRNCMP(ps, "static", 6) != 0)
573 {
574 semsg(_(e_command_cannot_be_shortened_str), ps);
575 break;
576 }
577 has_static = TRUE;
578 p = skipwhite(ps + 6);
579 }
580
Bram Moolenaard505d172022-12-18 21:42:55 +0000581 // object members (public, read access, private):
582 // "this._varname"
583 // "this.varname"
584 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000585 if (STRNCMP(p, "this", 4) == 0)
586 {
587 if (p[4] != '.' || !eval_isnamec1(p[5]))
588 {
589 semsg(_(e_invalid_object_member_declaration_str), p);
590 break;
591 }
592 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +0000593 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +0000594 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +0000595 char_u *init_expr = NULL;
596 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +0000597 &varname_end, &type_list, &type,
598 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000599 break;
600 if (add_member(&objmembers, varname, varname_end,
601 has_public, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +0000602 {
Bram Moolenaard505d172022-12-18 21:42:55 +0000603 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000604 break;
605 }
Bram Moolenaard505d172022-12-18 21:42:55 +0000606 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000607
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000608 // constructors:
609 // def new()
610 // enddef
611 // def newOther()
612 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000613 // object methods and class functions:
614 // def SomeMethod()
615 // enddef
616 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000617 // enddef
618 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000619 // def <Tval> someMethod()
620 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000621 else if (checkforcmd(&p, "def", 3))
622 {
623 exarg_T ea;
624 garray_T lines_to_free;
625
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000626 // TODO: error for "public static def Func()"?
627
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000628 CLEAR_FIELD(ea);
629 ea.cmd = line;
630 ea.arg = p;
631 ea.cmdidx = CMD_def;
632 ea.getline = eap->getline;
633 ea.cookie = eap->cookie;
634
635 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Bram Moolenaar554d0312023-01-05 19:59:18 +0000636 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
637 is_class ? CF_CLASS : CF_INTERFACE);
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000638 ga_clear_strings(&lines_to_free);
639
Bram Moolenaar6acf7572023-01-01 19:53:30 +0000640 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000641 {
Bram Moolenaar58b40092023-01-11 15:59:05 +0000642 char_u *name = uf->uf_name;
643 int is_new = STRNCMP(name, "new", 3) == 0;
Bram Moolenaar24a8d062023-01-14 13:12:06 +0000644 if (is_new && is_abstract)
645 {
646 emsg(_(e_cannot_define_new_function_in_abstract_class));
647 success = FALSE;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200648 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +0000649 break;
650 }
Bram Moolenaar6acf7572023-01-01 19:53:30 +0000651 garray_T *fgap = has_static || is_new
652 ? &classfunctions : &objmethods;
Bram Moolenaar58b40092023-01-11 15:59:05 +0000653 // Check the name isn't used already.
654 for (int i = 0; i < fgap->ga_len; ++i)
655 {
656 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
657 if (STRCMP(name, n) == 0)
658 {
659 semsg(_(e_duplicate_function_str), name);
660 break;
661 }
662 }
663
Bram Moolenaar6acf7572023-01-01 19:53:30 +0000664 if (ga_grow(fgap, 1) == OK)
665 {
666 if (is_new)
667 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000668
Bram Moolenaar6acf7572023-01-01 19:53:30 +0000669 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
670 ++fgap->ga_len;
671 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000672 }
673 }
674
675 // class members
676 else if (has_static)
677 {
678 // class members (public, read access, private):
679 // "static _varname"
680 // "static varname"
681 // "public static varname"
682 char_u *varname = p;
683 char_u *varname_end = NULL;
684 type_T *type = NULL;
685 char_u *init_expr = NULL;
686 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +0000687 &varname_end, &type_list, &type,
688 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000689 break;
690 if (add_member(&classmembers, varname, varname_end,
691 has_public, type, init_expr) == FAIL)
692 {
693 vim_free(init_expr);
694 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000695 }
696 }
697
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000698 else
699 {
Bram Moolenaar554d0312023-01-05 19:59:18 +0000700 if (is_class)
701 semsg(_(e_not_valid_command_in_class_str), line);
702 else
703 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000704 break;
705 }
706 }
707 vim_free(theline);
708
Bram Moolenaar83677162023-01-08 19:54:10 +0000709 class_T *extends_cl = NULL; // class from "extends" argument
710
711 /*
712 * Check a few things before defining the class.
713 */
714
715 // Check the "extends" class is valid.
716 if (success && extends != NULL)
717 {
718 typval_T tv;
719 tv.v_type = VAR_UNKNOWN;
Bram Moolenaara86655a2023-01-12 17:06:27 +0000720 if (eval_variable_import(extends, &tv) == FAIL)
Bram Moolenaar83677162023-01-08 19:54:10 +0000721 {
722 semsg(_(e_class_name_not_found_str), extends);
723 success = FALSE;
724 }
725 else
726 {
727 if (tv.v_type != VAR_CLASS
728 || tv.vval.v_class == NULL
729 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
730 {
731 semsg(_(e_cannot_extend_str), extends);
732 success = FALSE;
733 }
734 else
735 {
736 extends_cl = tv.vval.v_class;
737 ++extends_cl->class_refcount;
738 }
739 clear_tv(&tv);
740 }
741 }
742 VIM_CLEAR(extends);
743
Bram Moolenaara94bd9d2023-01-12 15:01:32 +0000744 class_T **intf_classes = NULL;
745
Bram Moolenaar83677162023-01-08 19:54:10 +0000746 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +0000747 if (success && ga_impl.ga_len > 0)
748 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +0000749 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
750
Bram Moolenaar94674f22023-01-06 18:42:20 +0000751 for (int i = 0; i < ga_impl.ga_len && success; ++i)
752 {
753 char_u *impl = ((char_u **)ga_impl.ga_data)[i];
754 typval_T tv;
755 tv.v_type = VAR_UNKNOWN;
Bram Moolenaara86655a2023-01-12 17:06:27 +0000756 if (eval_variable_import(impl, &tv) == FAIL)
Bram Moolenaar94674f22023-01-06 18:42:20 +0000757 {
758 semsg(_(e_interface_name_not_found_str), impl);
759 success = FALSE;
760 break;
761 }
762
763 if (tv.v_type != VAR_CLASS
764 || tv.vval.v_class == NULL
765 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
766 {
767 semsg(_(e_not_valid_interface_str), impl);
768 success = FALSE;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200769 clear_tv(&tv);
770 break;
Bram Moolenaar94674f22023-01-06 18:42:20 +0000771 }
772
Bram Moolenaar94674f22023-01-06 18:42:20 +0000773 class_T *ifcl = tv.vval.v_class;
Bram Moolenaara94bd9d2023-01-12 15:01:32 +0000774 intf_classes[i] = ifcl;
775 ++ifcl->class_refcount;
776
777 // check the members of the interface match the members of the class
Bram Moolenaar94674f22023-01-06 18:42:20 +0000778 for (int loop = 1; loop <= 2 && success; ++loop)
779 {
780 // loop == 1: check class members
781 // loop == 2: check object members
782 int if_count = loop == 1 ? ifcl->class_class_member_count
783 : ifcl->class_obj_member_count;
784 if (if_count == 0)
785 continue;
786 ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
787 : ifcl->class_obj_members;
788 ocmember_T *cl_ms = (ocmember_T *)(loop == 1
789 ? classmembers.ga_data
790 : objmembers.ga_data);
791 int cl_count = loop == 1 ? classmembers.ga_len
792 : objmembers.ga_len;
793 for (int if_i = 0; if_i < if_count; ++if_i)
794 {
795 int cl_i;
796 for (cl_i = 0; cl_i < cl_count; ++cl_i)
797 {
LemonBoyc5d27442023-08-19 13:02:35 +0200798 ocmember_T *m = &cl_ms[cl_i];
799 where_T where = WHERE_INIT;
800
801 if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) != 0)
802 continue;
803
804 // Ensure the type is matching.
Christian Brabandt28ae5a32023-08-19 14:09:16 +0200805 where.wt_func_name = (char *)m->ocm_name;
LemonBoyc5d27442023-08-19 13:02:35 +0200806 where.wt_kind = WT_MEMBER;
807 if (check_type_maybe(if_ms[if_i].ocm_type, m->ocm_type, TRUE,
808 where) != OK)
809 success = FALSE;
810
811 break;
Bram Moolenaar94674f22023-01-06 18:42:20 +0000812 }
813 if (cl_i == cl_count)
814 {
815 semsg(_(e_member_str_of_interface_str_not_implemented),
816 if_ms[if_i].ocm_name, impl);
817 success = FALSE;
818 break;
819 }
820 }
821 }
822
823 // check the functions/methods of the interface match the
824 // functions/methods of the class
825 for (int loop = 1; loop <= 2 && success; ++loop)
826 {
827 // loop == 1: check class functions
828 // loop == 2: check object methods
829 int if_count = loop == 1 ? ifcl->class_class_function_count
830 : ifcl->class_obj_method_count;
831 if (if_count == 0)
832 continue;
833 ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
834 : ifcl->class_obj_methods;
835 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
836 ? classfunctions.ga_data
837 : objmethods.ga_data);
838 int cl_count = loop == 1 ? classfunctions.ga_len
839 : objmethods.ga_len;
840 for (int if_i = 0; if_i < if_count; ++if_i)
841 {
842 char_u *if_name = if_fp[if_i]->uf_name;
843 int cl_i;
844 for (cl_i = 0; cl_i < cl_count; ++cl_i)
845 {
846 char_u *cl_name = cl_fp[cl_i]->uf_name;
847 if (STRCMP(if_name, cl_name) == 0)
848 {
LemonBoyc5d27442023-08-19 13:02:35 +0200849 where_T where = WHERE_INIT;
850
851 // Ensure the type is matching.
852 where.wt_func_name = (char *)if_name;
853 where.wt_kind = WT_METHOD;
854 if (check_type_maybe(if_fp[if_i]->uf_func_type,
855 cl_fp[cl_i]->uf_func_type, TRUE, where) != OK)
856 success = FALSE;
Bram Moolenaar94674f22023-01-06 18:42:20 +0000857 break;
858 }
859 }
860 if (cl_i == cl_count)
861 {
862 semsg(_(e_function_str_of_interface_str_not_implemented),
863 if_name, impl);
864 success = FALSE;
865 break;
866 }
867 }
868 }
869
870 clear_tv(&tv);
871 }
872 }
873
Bram Moolenaard40f00c2023-01-13 17:36:49 +0000874 if (success)
875 {
h-east61378a12023-04-18 19:07:29 +0100876 // Check no function argument name is used as a class member.
877 // (Object members are always accessed with "this." prefix, so no need
878 // to check them.)
Bram Moolenaard40f00c2023-01-13 17:36:49 +0000879 for (int loop = 1; loop <= 2 && success; ++loop)
880 {
881 garray_T *gap = loop == 1 ? &classfunctions : &objmethods;
882
883 for (int fi = 0; fi < gap->ga_len && success; ++fi)
884 {
885 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
886
887 for (int i = 0; i < uf->uf_args.ga_len && success; ++i)
888 {
889 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
h-east61378a12023-04-18 19:07:29 +0100890 garray_T *mgap = &classmembers;
891
892 for (int mi = 0; mi < mgap->ga_len; ++mi)
Bram Moolenaard40f00c2023-01-13 17:36:49 +0000893 {
h-east61378a12023-04-18 19:07:29 +0100894 char_u *mname = ((ocmember_T *)mgap->ga_data + mi)
895 ->ocm_name;
896 if (STRCMP(aname, mname) == 0)
Bram Moolenaard40f00c2023-01-13 17:36:49 +0000897 {
h-east61378a12023-04-18 19:07:29 +0100898 success = FALSE;
899
900 if (uf->uf_script_ctx.sc_sid > 0)
901 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
902
903 semsg(_(e_argument_already_declared_in_class_str),
Bram Moolenaard40f00c2023-01-13 17:36:49 +0000904 aname);
h-east61378a12023-04-18 19:07:29 +0100905 break;
Bram Moolenaard40f00c2023-01-13 17:36:49 +0000906 }
907 }
908 }
909 }
910 }
911 }
912
913
Bram Moolenaareb533502022-12-14 15:06:11 +0000914 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000915 if (success)
916 {
Bram Moolenaard505d172022-12-18 21:42:55 +0000917 // "endclass" encountered without failures: Create the class.
918
Bram Moolenaareb533502022-12-14 15:06:11 +0000919 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000920 if (cl == NULL)
921 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +0000922 if (!is_class)
923 cl->class_flags = CLASS_INTERFACE;
924
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000925 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +0000926 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +0000927 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000928 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +0000929
Bram Moolenaard0200c82023-01-28 15:19:40 +0000930 if (extends_cl != NULL)
931 {
932 cl->class_extends = extends_cl;
933 extends_cl->class_flags |= CLASS_EXTENDED;
934 }
Bram Moolenaar83677162023-01-08 19:54:10 +0000935
Bram Moolenaard505d172022-12-18 21:42:55 +0000936 // Add class and object members to "cl".
937 if (add_members_to_class(&classmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +0000938 extends_cl == NULL ? NULL
939 : extends_cl->class_class_members,
940 extends_cl == NULL ? 0
941 : extends_cl->class_class_member_count,
942 &cl->class_class_members,
943 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +0000944 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +0000945 extends_cl == NULL ? NULL
946 : extends_cl->class_obj_members,
947 extends_cl == NULL ? 0
948 : extends_cl->class_obj_member_count,
949 &cl->class_obj_members,
950 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000951 goto cleanup;
952
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000953 if (ga_impl.ga_len > 0)
954 {
955 // Move the "implements" names into the class.
956 cl->class_interface_count = ga_impl.ga_len;
957 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
958 if (cl->class_interfaces == NULL)
959 goto cleanup;
960 for (int i = 0; i < ga_impl.ga_len; ++i)
961 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
962 VIM_CLEAR(ga_impl.ga_data);
963 ga_impl.ga_len = 0;
964
Bram Moolenaard0200c82023-01-28 15:19:40 +0000965 cl->class_interfaces_cl = intf_classes;
966 intf_classes = NULL;
967 }
968
969 if (cl->class_interface_count > 0 || extends_cl != NULL)
970 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200971 // For each interface add a lookup table for the member index on
972 // the interface to the member index in this class.
973 // And a lookup table for the object method index on the interface
Bram Moolenaard0200c82023-01-28 15:19:40 +0000974 // to the object method index in this class.
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200975 for (int i = 0; i < cl->class_interface_count; ++i)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000976 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200977 class_T *ifcl = cl->class_interfaces_cl[i];
Bram Moolenaard0200c82023-01-28 15:19:40 +0000978
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200979 if (update_member_method_lookup_table(ifcl, cl, &objmethods,
980 0, TRUE) == FAIL)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000981 goto cleanup;
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200982 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000983
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200984 // Update the lookup table for the extended class, if nay
985 if (extends_cl != NULL)
986 {
987 class_T *pclass = extends_cl;
988 int pobj_method_offset = objmethods.ga_len;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000989
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200990 // Update the entire lineage of extended classes.
991 while (pclass != NULL)
Bram Moolenaard0200c82023-01-28 15:19:40 +0000992 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200993 if (update_member_method_lookup_table(pclass, cl,
994 &objmethods, pobj_method_offset, FALSE) ==
995 FAIL)
996 goto cleanup;
Bram Moolenaard0200c82023-01-28 15:19:40 +0000997
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200998 pobj_method_offset += pclass->class_obj_method_count_child;
999 pclass = pclass->class_extends;
Bram Moolenaard0200c82023-01-28 15:19:40 +00001000 }
1001 }
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001002 }
1003
Bram Moolenaar554d0312023-01-05 19:59:18 +00001004 if (is_class && cl->class_class_member_count > 0)
Bram Moolenaard505d172022-12-18 21:42:55 +00001005 {
1006 // Allocate a typval for each class member and initialize it.
1007 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
1008 cl->class_class_member_count);
1009 if (cl->class_members_tv != NULL)
1010 for (int i = 0; i < cl->class_class_member_count; ++i)
1011 {
1012 ocmember_T *m = &cl->class_class_members[i];
1013 typval_T *tv = &cl->class_members_tv[i];
1014 if (m->ocm_init != NULL)
1015 {
1016 typval_T *etv = eval_expr(m->ocm_init, eap);
1017 if (etv != NULL)
1018 {
1019 *tv = *etv;
1020 vim_free(etv);
1021 }
1022 }
1023 else
1024 {
1025 // TODO: proper default value
1026 tv->v_type = m->ocm_type->tt_type;
1027 tv->vval.v_string = NULL;
1028 }
1029 }
1030 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001031
1032 int have_new = FALSE;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001033 for (int i = 0; i < classfunctions.ga_len; ++i)
1034 if (STRCMP(((ufunc_T **)classfunctions.ga_data)[i]->uf_name,
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001035 "new") == 0)
1036 {
1037 have_new = TRUE;
1038 break;
1039 }
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001040 if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001041 {
1042 // No new() method was defined, add the default constructor.
1043 garray_T fga;
1044 ga_init2(&fga, 1, 1000);
1045 ga_concat(&fga, (char_u *)"new(");
1046 for (int i = 0; i < cl->class_obj_member_count; ++i)
1047 {
1048 if (i > 0)
1049 ga_concat(&fga, (char_u *)", ");
1050 ga_concat(&fga, (char_u *)"this.");
Bram Moolenaard505d172022-12-18 21:42:55 +00001051 ocmember_T *m = cl->class_obj_members + i;
1052 ga_concat(&fga, (char_u *)m->ocm_name);
Bram Moolenaar65b0d162022-12-13 18:43:22 +00001053 ga_concat(&fga, (char_u *)" = v:none");
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001054 }
1055 ga_concat(&fga, (char_u *)")\nenddef\n");
1056 ga_append(&fga, NUL);
1057
1058 exarg_T fea;
1059 CLEAR_FIELD(fea);
1060 fea.cmdidx = CMD_def;
1061 fea.cmd = fea.arg = fga.ga_data;
1062
1063 garray_T lines_to_free;
1064 ga_init2(&lines_to_free, sizeof(char_u *), 50);
1065
Bram Moolenaar2c011312023-01-07 10:51:30 +00001066 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001067
1068 ga_clear_strings(&lines_to_free);
1069 vim_free(fga.ga_data);
1070
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001071 if (nf != NULL && ga_grow(&classfunctions, 1) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001072 {
Bram Moolenaar58b40092023-01-11 15:59:05 +00001073 ((ufunc_T **)classfunctions.ga_data)[classfunctions.ga_len]
1074 = nf;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001075 ++classfunctions.ga_len;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001076
1077 nf->uf_flags |= FC_NEW;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001078 nf->uf_ret_type = get_type_ptr(&type_list);
1079 if (nf->uf_ret_type != NULL)
1080 {
1081 nf->uf_ret_type->tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001082 nf->uf_ret_type->tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001083 nf->uf_ret_type->tt_argcount = 0;
1084 nf->uf_ret_type->tt_args = NULL;
1085 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001086 }
1087 }
1088
Bram Moolenaar58b40092023-01-11 15:59:05 +00001089 // Move all the functions into the created class.
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001090 // loop 1: class functions, loop 2: object methods
1091 for (int loop = 1; loop <= 2; ++loop)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001092 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001093 garray_T *gap = loop == 1 ? &classfunctions : &objmethods;
1094 int *fcount = loop == 1 ? &cl->class_class_function_count
1095 : &cl->class_obj_method_count;
1096 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
1097 : &cl->class_obj_methods;
1098
Bram Moolenaar83677162023-01-08 19:54:10 +00001099 int parent_count = 0;
1100 if (extends_cl != NULL)
1101 // Include functions from the parent.
1102 parent_count = loop == 1
1103 ? extends_cl->class_class_function_count
1104 : extends_cl->class_obj_method_count;
1105
1106 *fcount = parent_count + gap->ga_len;
1107 if (*fcount == 0)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001108 {
1109 *fup = NULL;
1110 continue;
1111 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001112 *fup = ALLOC_MULT(ufunc_T *, *fcount);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001113 if (*fup == NULL)
1114 goto cleanup;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001115
Ernie Rael114ec812023-06-04 18:11:35 +01001116 if (gap->ga_len != 0)
1117 mch_memmove(*fup, gap->ga_data,
1118 sizeof(ufunc_T *) * gap->ga_len);
Bram Moolenaar58b40092023-01-11 15:59:05 +00001119 vim_free(gap->ga_data);
1120 if (loop == 1)
1121 cl->class_class_function_count_child = gap->ga_len;
1122 else
1123 cl->class_obj_method_count_child = gap->ga_len;
1124
Bram Moolenaar83677162023-01-08 19:54:10 +00001125 int skipped = 0;
1126 for (int i = 0; i < parent_count; ++i)
1127 {
1128 // Copy functions from the parent. Can't use the same
1129 // function, because "uf_class" is different and compilation
1130 // will have a different result.
Bram Moolenaar58b40092023-01-11 15:59:05 +00001131 // Put them after the functions in the current class, object
1132 // methods may be overruled, then "super.Method()" is used to
1133 // find a method from the parent.
Bram Moolenaar83677162023-01-08 19:54:10 +00001134 // Skip "new" functions. TODO: not all of them.
1135 if (loop == 1 && STRNCMP(
1136 extends_cl->class_class_functions[i]->uf_name,
1137 "new", 3) == 0)
1138 ++skipped;
1139 else
Bram Moolenaar58b40092023-01-11 15:59:05 +00001140 {
1141 ufunc_T *pf = (loop == 1
Bram Moolenaar83677162023-01-08 19:54:10 +00001142 ? extends_cl->class_class_functions
Bram Moolenaar58b40092023-01-11 15:59:05 +00001143 : extends_cl->class_obj_methods)[i];
1144 (*fup)[gap->ga_len + i - skipped] = copy_function(pf);
1145
1146 // If the child class overrides a function from the parent
1147 // the signature must be equal.
1148 char_u *pname = pf->uf_name;
1149 for (int ci = 0; ci < gap->ga_len; ++ci)
1150 {
1151 ufunc_T *cf = (*fup)[ci];
1152 char_u *cname = cf->uf_name;
1153 if (STRCMP(pname, cname) == 0)
1154 {
1155 where_T where = WHERE_INIT;
1156 where.wt_func_name = (char *)pname;
LemonBoyc5d27442023-08-19 13:02:35 +02001157 where.wt_kind = WT_METHOD;
Bram Moolenaar58b40092023-01-11 15:59:05 +00001158 (void)check_type(pf->uf_func_type, cf->uf_func_type,
1159 TRUE, where);
1160 }
1161 }
1162 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001163 }
1164
Bram Moolenaar83677162023-01-08 19:54:10 +00001165 *fcount -= skipped;
1166
1167 // Set the class pointer on all the functions and object methods.
1168 for (int i = 0; i < *fcount; ++i)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001169 {
1170 ufunc_T *fp = (*fup)[i];
1171 fp->uf_class = cl;
1172 if (loop == 2)
1173 fp->uf_flags |= FC_OBJECT;
1174 }
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001175 }
1176
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001177 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001178 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001179 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001180 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001181 cl->class_type_list = type_list;
1182
1183 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001184 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001185
1186 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001187 // TODO: handle other context, e.g. in a function
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001188 typval_T tv;
1189 tv.v_type = VAR_CLASS;
1190 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001191 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001192 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001193 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001194 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001195 return;
1196 }
1197
1198cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00001199 if (cl != NULL)
1200 {
1201 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001202 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001203 if (cl->class_interfaces != NULL)
1204 {
1205 for (int i = 0; i < cl->class_interface_count; ++i)
1206 vim_free(cl->class_interfaces[i]);
1207 vim_free(cl->class_interfaces);
1208 }
1209 if (cl->class_interfaces_cl != NULL)
1210 {
1211 for (int i = 0; i < cl->class_interface_count; ++i)
1212 class_unref(cl->class_interfaces_cl[i]);
1213 vim_free(cl->class_interfaces_cl);
1214 }
Bram Moolenaareb533502022-12-14 15:06:11 +00001215 vim_free(cl->class_obj_members);
1216 vim_free(cl->class_obj_methods);
1217 vim_free(cl);
1218 }
1219
Bram Moolenaar83677162023-01-08 19:54:10 +00001220 vim_free(extends);
1221 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001222
1223 if (intf_classes != NULL)
1224 {
1225 for (int i = 0; i < ga_impl.ga_len; ++i)
1226 class_unref(intf_classes[i]);
1227 vim_free(intf_classes);
1228 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001229 ga_clear_strings(&ga_impl);
1230
Bram Moolenaard505d172022-12-18 21:42:55 +00001231 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001232 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001233 garray_T *gap = round == 1 ? &classmembers : &objmembers;
1234 if (gap->ga_len == 0 || gap->ga_data == NULL)
1235 continue;
1236
1237 for (int i = 0; i < gap->ga_len; ++i)
1238 {
1239 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
1240 vim_free(m->ocm_name);
1241 vim_free(m->ocm_init);
1242 }
1243 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001244 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001245
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001246 for (int i = 0; i < objmethods.ga_len; ++i)
1247 {
1248 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
1249 func_clear_free(uf, FALSE);
1250 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001251 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001252
1253 for (int i = 0; i < classfunctions.ga_len; ++i)
1254 {
1255 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
1256 func_clear_free(uf, FALSE);
1257 }
1258 ga_clear(&classfunctions);
1259
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001260 clear_type_list(&type_list);
1261}
1262
1263/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001264 * Find member "name" in class "cl", set "member_idx" to the member index and
1265 * return its type.
1266 * When not found "member_idx" is set to -1 and t_any is returned.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001267 */
1268 type_T *
1269class_member_type(
1270 class_T *cl,
1271 char_u *name,
1272 char_u *name_end,
1273 int *member_idx)
1274{
1275 *member_idx = -1; // not found (yet)
1276 size_t len = name_end - name;
1277
1278 for (int i = 0; i < cl->class_obj_member_count; ++i)
1279 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001280 ocmember_T *m = cl->class_obj_members + i;
1281 if (STRNCMP(m->ocm_name, name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001282 {
1283 *member_idx = i;
Bram Moolenaard505d172022-12-18 21:42:55 +00001284 return m->ocm_type;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001285 }
1286 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001287
1288 semsg(_(e_unknown_variable_str), name);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001289 return &t_any;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001290}
1291
1292/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001293 * Handle ":enum" up to ":endenum".
1294 */
1295 void
1296ex_enum(exarg_T *eap UNUSED)
1297{
1298 // TODO
1299}
1300
1301/*
1302 * Handle ":type".
1303 */
1304 void
1305ex_type(exarg_T *eap UNUSED)
1306{
1307 // TODO
1308}
1309
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001310/*
1311 * Evaluate what comes after a class:
1312 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001313 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001314 * - class constructor: SomeClass.new()
1315 * - object member: someObject.varname
1316 * - object method: someObject.SomeMethod()
1317 *
1318 * "*arg" points to the '.'.
1319 * "*arg" is advanced to after the member name or method call.
1320 *
1321 * Returns FAIL or OK.
1322 */
1323 int
1324class_object_index(
1325 char_u **arg,
1326 typval_T *rettv,
1327 evalarg_T *evalarg,
1328 int verbose UNUSED) // give error messages
1329{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001330 if (VIM_ISWHITE((*arg)[1]))
1331 {
1332 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
1333 return FAIL;
1334 }
1335
1336 ++*arg;
1337 char_u *name = *arg;
1338 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
1339 if (name_end == name)
1340 return FAIL;
1341 size_t len = name_end - name;
1342
Bram Moolenaar552bdca2023-02-17 21:08:50 +00001343 class_T *cl;
1344 if (rettv->v_type == VAR_CLASS)
1345 cl = rettv->vval.v_class;
1346 else // VAR_OBJECT
1347 {
1348 if (rettv->vval.v_object == NULL)
1349 {
1350 emsg(_(e_using_null_object));
1351 return FAIL;
1352 }
1353 cl = rettv->vval.v_object->obj_class;
1354 }
1355
Bram Moolenaard13dd302023-03-11 20:56:35 +00001356 if (cl == NULL)
1357 {
1358 emsg(_(e_incomplete_type));
1359 return FAIL;
1360 }
1361
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001362 if (*name_end == '(')
1363 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001364 int on_class = rettv->v_type == VAR_CLASS;
1365 int count = on_class ? cl->class_class_function_count
1366 : cl->class_obj_method_count;
1367 for (int i = 0; i < count; ++i)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001368 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001369 ufunc_T *fp = on_class ? cl->class_class_functions[i]
1370 : cl->class_obj_methods[i];
Bram Moolenaar4ae00572022-12-09 22:49:23 +00001371 // Use a separate pointer to avoid that ASAN complains about
1372 // uf_name[] only being 4 characters.
1373 char_u *ufname = (char_u *)fp->uf_name;
1374 if (STRNCMP(name, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001375 {
1376 typval_T argvars[MAX_FUNC_ARGS + 1];
1377 int argcount = 0;
1378
1379 char_u *argp = name_end;
1380 int ret = get_func_arguments(&argp, evalarg, 0,
1381 argvars, &argcount);
1382 if (ret == FAIL)
1383 return FAIL;
1384
1385 funcexe_T funcexe;
1386 CLEAR_FIELD(funcexe);
1387 funcexe.fe_evaluate = TRUE;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001388 if (rettv->v_type == VAR_OBJECT)
1389 {
1390 funcexe.fe_object = rettv->vval.v_object;
1391 ++funcexe.fe_object->obj_refcount;
1392 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001393
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001394 // Clear the class or object after calling the function, in
1395 // case the refcount is one.
1396 typval_T tv_tofree = *rettv;
1397 rettv->v_type = VAR_UNKNOWN;
1398
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001399 // Call the user function. Result goes into rettv;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001400 int error = call_user_func_check(fp, argcount, argvars,
1401 rettv, &funcexe, NULL);
1402
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001403 // Clear the previous rettv and the arguments.
1404 clear_tv(&tv_tofree);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001405 for (int idx = 0; idx < argcount; ++idx)
1406 clear_tv(&argvars[idx]);
1407
1408 if (error != FCERR_NONE)
1409 {
1410 user_func_error(error, printable_func_name(fp),
1411 funcexe.fe_found_var);
1412 return FAIL;
1413 }
1414 *arg = argp;
1415 return OK;
1416 }
1417 }
1418
1419 semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name);
1420 }
1421
1422 else if (rettv->v_type == VAR_OBJECT)
1423 {
1424 for (int i = 0; i < cl->class_obj_member_count; ++i)
1425 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001426 ocmember_T *m = &cl->class_obj_members[i];
1427 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001428 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001429 if (*name == '_')
1430 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001431 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001432 return FAIL;
1433 }
1434
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001435 // The object only contains a pointer to the class, the member
1436 // values array follows right after that.
1437 object_T *obj = rettv->vval.v_object;
1438 typval_T *tv = (typval_T *)(obj + 1) + i;
1439 copy_tv(tv, rettv);
1440 object_unref(obj);
1441
1442 *arg = name_end;
1443 return OK;
1444 }
1445 }
1446
1447 semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
1448 }
1449
Bram Moolenaard505d172022-12-18 21:42:55 +00001450 else if (rettv->v_type == VAR_CLASS)
1451 {
1452 // class member
1453 for (int i = 0; i < cl->class_class_member_count; ++i)
1454 {
1455 ocmember_T *m = &cl->class_class_members[i];
1456 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
1457 {
1458 if (*name == '_')
1459 {
1460 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
1461 return FAIL;
1462 }
1463
1464 typval_T *tv = &cl->class_members_tv[i];
1465 copy_tv(tv, rettv);
1466 class_unref(cl);
1467
1468 *arg = name_end;
1469 return OK;
1470 }
1471 }
1472
1473 semsg(_(e_member_not_found_on_class_str_str), cl->class_name, name);
1474 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001475
1476 return FAIL;
1477}
1478
1479/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001480 * If "arg" points to a class or object method, return it.
1481 * Otherwise return NULL.
1482 */
1483 ufunc_T *
1484find_class_func(char_u **arg)
1485{
1486 char_u *name = *arg;
1487 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
1488 if (name_end == name || *name_end != '.')
1489 return NULL;
1490
1491 size_t len = name_end - name;
1492 typval_T tv;
1493 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00001494 if (eval_variable(name, (int)len,
1495 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001496 return NULL;
1497 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00001498 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001499
1500 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
1501 : tv.vval.v_object->obj_class;
1502 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00001503 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001504 char_u *fname = name_end + 1;
1505 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
1506 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00001507 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001508 len = fname_end - fname;
1509
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001510 int count = tv.v_type == VAR_CLASS ? cl->class_class_function_count
1511 : cl->class_obj_method_count;
1512 ufunc_T **funcs = tv.v_type == VAR_CLASS ? cl->class_class_functions
1513 : cl->class_obj_methods;
1514 for (int i = 0; i < count; ++i)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001515 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001516 ufunc_T *fp = funcs[i];
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001517 // Use a separate pointer to avoid that ASAN complains about
1518 // uf_name[] only being 4 characters.
1519 char_u *ufname = (char_u *)fp->uf_name;
1520 if (STRNCMP(fname, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaareb533502022-12-14 15:06:11 +00001521 {
1522 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001523 return fp;
Bram Moolenaareb533502022-12-14 15:06:11 +00001524 }
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001525 }
1526
Bram Moolenaareb533502022-12-14 15:06:11 +00001527fail_after_eval:
1528 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001529 return NULL;
1530}
1531
1532/*
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001533 * If "name[len]" is a class member in cctx->ctx_ufunc->uf_class return the
1534 * index in class.class_class_members[].
1535 * If "cl_ret" is not NULL set it to the class.
1536 * Otherwise return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001537 */
1538 int
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001539class_member_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001540{
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001541 if (cctx == NULL || cctx->ctx_ufunc == NULL
1542 || cctx->ctx_ufunc->uf_class == NULL)
1543 return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001544 class_T *cl = cctx->ctx_ufunc->uf_class;
1545
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001546 for (int i = 0; i < cl->class_class_member_count; ++i)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001547 {
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001548 ocmember_T *m = &cl->class_class_members[i];
1549 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001550 {
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001551 if (cl_ret != NULL)
1552 *cl_ret = cl;
1553 return i;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001554 }
1555 }
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001556 return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001557}
1558
1559/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00001560 * Return TRUE if current context "cctx_arg" is inside class "cl".
1561 * Return FALSE if not.
1562 */
1563 int
1564inside_class(cctx_T *cctx_arg, class_T *cl)
1565{
1566 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
1567 if (cctx->ctx_ufunc != NULL && cctx->ctx_ufunc->uf_class == cl)
1568 return TRUE;
1569 return FALSE;
1570}
1571
1572/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001573 * Make a copy of an object.
1574 */
1575 void
1576copy_object(typval_T *from, typval_T *to)
1577{
1578 *to = *from;
1579 if (to->vval.v_object != NULL)
1580 ++to->vval.v_object->obj_refcount;
1581}
1582
1583/*
1584 * Free an object.
1585 */
1586 static void
1587object_clear(object_T *obj)
1588{
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01001589 // Avoid a recursive call, it can happen if "obj" has a circular reference.
1590 obj->obj_refcount = INT_MAX;
1591
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001592 class_T *cl = obj->obj_class;
1593
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02001594 if (!cl)
1595 return;
1596
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001597 // the member values are just after the object structure
1598 typval_T *tv = (typval_T *)(obj + 1);
1599 for (int i = 0; i < cl->class_obj_member_count; ++i)
1600 clear_tv(tv + i);
1601
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001602 // Remove from the list headed by "first_object".
1603 object_cleared(obj);
1604
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001605 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001606 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001607}
1608
1609/*
1610 * Unreference an object.
1611 */
1612 void
1613object_unref(object_T *obj)
1614{
1615 if (obj != NULL && --obj->obj_refcount <= 0)
1616 object_clear(obj);
1617}
1618
1619/*
1620 * Make a copy of a class.
1621 */
1622 void
1623copy_class(typval_T *from, typval_T *to)
1624{
1625 *to = *from;
1626 if (to->vval.v_class != NULL)
1627 ++to->vval.v_class->class_refcount;
1628}
1629
1630/*
1631 * Unreference a class. Free it when the reference count goes down to zero.
1632 */
1633 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001634class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001635{
Bram Moolenaard505d172022-12-18 21:42:55 +00001636 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001637 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001638 // Freeing what the class contains may recursively come back here.
1639 // Clear "class_name" first, if it is NULL the class does not need to
1640 // be freed.
1641 VIM_CLEAR(cl->class_name);
1642
Bram Moolenaar83677162023-01-08 19:54:10 +00001643 class_unref(cl->class_extends);
1644
Bram Moolenaar94674f22023-01-06 18:42:20 +00001645 for (int i = 0; i < cl->class_interface_count; ++i)
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001646 {
Bram Moolenaar94674f22023-01-06 18:42:20 +00001647 vim_free(((char_u **)cl->class_interfaces)[i]);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001648 if (cl->class_interfaces_cl[i] != NULL)
1649 class_unref(cl->class_interfaces_cl[i]);
1650 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001651 vim_free(cl->class_interfaces);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001652 vim_free(cl->class_interfaces_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001653
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001654 itf2class_T *next;
1655 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
1656 {
1657 next = i2c->i2c_next;
1658 vim_free(i2c);
1659 }
1660
Bram Moolenaard505d172022-12-18 21:42:55 +00001661 for (int i = 0; i < cl->class_class_member_count; ++i)
1662 {
1663 ocmember_T *m = &cl->class_class_members[i];
1664 vim_free(m->ocm_name);
1665 vim_free(m->ocm_init);
1666 if (cl->class_members_tv != NULL)
1667 clear_tv(&cl->class_members_tv[i]);
1668 }
1669 vim_free(cl->class_class_members);
1670 vim_free(cl->class_members_tv);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001671
1672 for (int i = 0; i < cl->class_obj_member_count; ++i)
1673 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001674 ocmember_T *m = &cl->class_obj_members[i];
1675 vim_free(m->ocm_name);
1676 vim_free(m->ocm_init);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001677 }
1678 vim_free(cl->class_obj_members);
1679
Bram Moolenaarec8b74f2023-01-01 14:11:27 +00001680 for (int i = 0; i < cl->class_class_function_count; ++i)
1681 {
1682 ufunc_T *uf = cl->class_class_functions[i];
1683 func_clear_free(uf, FALSE);
1684 }
1685 vim_free(cl->class_class_functions);
1686
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001687 for (int i = 0; i < cl->class_obj_method_count; ++i)
1688 {
1689 ufunc_T *uf = cl->class_obj_methods[i];
1690 func_clear_free(uf, FALSE);
1691 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001692 vim_free(cl->class_obj_methods);
1693
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001694 clear_type_list(&cl->class_type_list);
1695
1696 vim_free(cl);
1697 }
1698}
1699
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001700static object_T *first_object = NULL;
1701
1702/*
1703 * Call this function when an object has been created. It will be added to the
1704 * list headed by "first_object".
1705 */
1706 void
1707object_created(object_T *obj)
1708{
1709 if (first_object != NULL)
1710 {
1711 obj->obj_next_used = first_object;
1712 first_object->obj_prev_used = obj;
1713 }
1714 first_object = obj;
1715}
1716
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01001717static object_T *next_nonref_obj = NULL;
1718
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001719/*
1720 * Call this function when an object has been cleared and is about to be freed.
1721 * It is removed from the list headed by "first_object".
1722 */
1723 void
1724object_cleared(object_T *obj)
1725{
1726 if (obj->obj_next_used != NULL)
1727 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
1728 if (obj->obj_prev_used != NULL)
1729 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
1730 else if (first_object == obj)
1731 first_object = obj->obj_next_used;
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01001732
1733 // update the next object to check if needed
1734 if (obj == next_nonref_obj)
1735 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001736}
1737
1738/*
1739 * Go through the list of all objects and free items without "copyID".
1740 */
1741 int
1742object_free_nonref(int copyID)
1743{
1744 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001745
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01001746 for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001747 {
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01001748 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001749 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
1750 {
1751 // Free the object and items it contains.
1752 object_clear(obj);
1753 did_free = TRUE;
1754 }
1755 }
1756
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01001757 next_nonref_obj = NULL;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001758 return did_free;
1759}
1760
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001761
1762#endif // FEAT_EVAL