blob: 165c3297f72a2bd467ae142ccf6381fef774a4d6 [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
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +020027 * variable name and "type_ret" is set to the declared or detected type.
Bram Moolenaard505d172022-12-18 21:42:55 +000028 * "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(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020033 exarg_T *eap,
34 char_u *line,
35 char_u *varname,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020036 int has_public, // TRUE if "public" seen before "varname"
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020037 char_u **varname_end,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +020038 garray_T *type_list,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +020039 type_T **type_ret,
40 char_u **init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +000041{
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
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +020070 char_u *init_arg = skipwhite(type_arg);
71 if (type == NULL && *init_arg != '=')
Bram Moolenaard505d172022-12-18 21:42:55 +000072 {
73 emsg(_(e_type_or_initialization_required));
74 return FAIL;
75 }
76
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +020077 if (init_expr == NULL && *init_arg == '=')
Bram Moolenaard505d172022-12-18 21:42:55 +000078 {
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +020079 emsg(_(e_cannot_initialize_member_in_interface));
80 return FAIL;
81 }
82
83 if (*init_arg == '=')
84 {
85 evalarg_T evalarg;
86 char_u *expr_start, *expr_end;
87
88 if (!VIM_ISWHITE(init_arg[-1]) || !VIM_ISWHITE(init_arg[1]))
Bram Moolenaard505d172022-12-18 21:42:55 +000089 {
90 semsg(_(e_white_space_required_before_and_after_str_at_str),
91 "=", type_arg);
92 return FAIL;
93 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +020094 init_arg = skipwhite(init_arg + 1);
Bram Moolenaard505d172022-12-18 21:42:55 +000095
Bram Moolenaard505d172022-12-18 21:42:55 +000096 fill_evalarg_from_eap(&evalarg, eap, FALSE);
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +020097 (void)skip_expr_concatenate(&init_arg, &expr_start, &expr_end, &evalarg);
Bram Moolenaard505d172022-12-18 21:42:55 +000098
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +020099 // No type specified for the member. Set it to "any" and the correct
100 // type will be set when the object is instantiated.
Bram Moolenaard505d172022-12-18 21:42:55 +0000101 if (type == NULL)
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200102 type = &t_any;
Bram Moolenaard505d172022-12-18 21:42:55 +0000103
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200104 *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
105 // Free the memory pointed by expr_start.
Bram Moolenaard505d172022-12-18 21:42:55 +0000106 clear_evalarg(&evalarg, NULL);
107 }
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +0200108 else if (!valid_declaration_type(type))
Bram Moolenaard505d172022-12-18 21:42:55 +0000109 return FAIL;
110
111 *type_ret = type;
Bram Moolenaard505d172022-12-18 21:42:55 +0000112 return OK;
113}
114
115/*
116 * Add a member to an object or a class.
117 * Returns OK when successful, "init_expr" will be consumed then.
118 * Returns FAIL otherwise, caller might need to free "init_expr".
119 */
120 static int
121add_member(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200122 garray_T *gap,
123 char_u *varname,
124 char_u *varname_end,
125 int has_public,
126 type_T *type,
127 char_u *init_expr)
Bram Moolenaard505d172022-12-18 21:42:55 +0000128{
129 if (ga_grow(gap, 1) == FAIL)
130 return FAIL;
131 ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
132 m->ocm_name = vim_strnsave(varname, varname_end - varname);
=?UTF-8?q?Ola=20S=C3=B6der?=d8742472023-03-05 13:12:32 +0000133 m->ocm_access = has_public ? VIM_ACCESS_ALL
134 : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
Bram Moolenaard505d172022-12-18 21:42:55 +0000135 m->ocm_type = type;
136 if (init_expr != NULL)
137 m->ocm_init = init_expr;
138 ++gap->ga_len;
139 return OK;
140}
141
142/*
143 * Move the class or object members found while parsing a class into the class.
144 * "gap" contains the found members.
Bram Moolenaar83677162023-01-08 19:54:10 +0000145 * "parent_members" points to the members in the parent class (if any)
146 * "parent_count" is the number of members in the parent class
Bram Moolenaard505d172022-12-18 21:42:55 +0000147 * "members" will be set to the newly allocated array of members and
148 * "member_count" set to the number of members.
149 * Returns OK or FAIL.
150 */
151 static int
152add_members_to_class(
153 garray_T *gap,
Bram Moolenaar83677162023-01-08 19:54:10 +0000154 ocmember_T *parent_members,
155 int parent_count,
Bram Moolenaard505d172022-12-18 21:42:55 +0000156 ocmember_T **members,
157 int *member_count)
158{
Bram Moolenaar83677162023-01-08 19:54:10 +0000159 *member_count = parent_count + gap->ga_len;
160 *members = *member_count == 0 ? NULL
161 : ALLOC_MULT(ocmember_T, *member_count);
162 if (*member_count > 0 && *members == NULL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000163 return FAIL;
Bram Moolenaar83677162023-01-08 19:54:10 +0000164 for (int i = 0; i < parent_count; ++i)
165 {
166 // parent members need to be copied
Bram Moolenaarae3205a2023-01-15 20:49:00 +0000167 ocmember_T *m = *members + i;
168 *m = parent_members[i];
169 m->ocm_name = vim_strsave(m->ocm_name);
170 if (m->ocm_init != NULL)
171 m->ocm_init = vim_strsave(m->ocm_init);
Bram Moolenaar83677162023-01-08 19:54:10 +0000172 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000173 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000174 // new members are moved
175 mch_memmove(*members + parent_count,
176 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000177 VIM_CLEAR(gap->ga_data);
178 return OK;
179}
180
181/*
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000182 * Convert a member index "idx" of interface "itf" to the member index of class
183 * "cl" implementing that interface.
184 */
185 int
Bram Moolenaard0200c82023-01-28 15:19:40 +0000186object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000187{
Bram Moolenaard0200c82023-01-28 15:19:40 +0000188 if (idx > (is_method ? itf->class_obj_method_count
189 : itf->class_obj_member_count))
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000190 {
191 siemsg("index %d out of range for interface %s", idx, itf->class_name);
192 return 0;
193 }
Yegappan Lakshmanan74cc13c2023-08-13 17:41:26 +0200194
195 // If "cl" is the interface or the class that is extended, then the method
196 // index can be used directly and there is no need to search for the method
197 // index in one of the child classes.
198 if (cl == itf)
199 return idx;
200
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000201 itf2class_T *i2c;
202 for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
Bram Moolenaard0200c82023-01-28 15:19:40 +0000203 if (i2c->i2c_class == cl && i2c->i2c_is_method == is_method)
Bram Moolenaar29ac5df2023-01-16 19:43:47 +0000204 break;
205 if (i2c == NULL)
206 {
207 siemsg("class %s not found on interface %s",
208 cl->class_name, itf->class_name);
209 return 0;
210 }
211 int *table = (int *)(i2c + 1);
212 return table[idx];
213}
214
215/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200216 * Check whether a class named "extends_name" is present. If the class is
217 * valid, then "extends_clp" is set with the class pointer.
218 * Returns TRUE if the class name "extends_names" is a valid class.
219 */
220 static int
221validate_extends_class(char_u *extends_name, class_T **extends_clp)
222{
223 typval_T tv;
224 int success = FALSE;
225
226 tv.v_type = VAR_UNKNOWN;
227 if (eval_variable_import(extends_name, &tv) == FAIL)
228 {
229 semsg(_(e_class_name_not_found_str), extends_name);
230 return success;
231 }
232 else
233 {
234 if (tv.v_type != VAR_CLASS
235 || tv.vval.v_class == NULL
236 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
237 semsg(_(e_cannot_extend_str), extends_name);
238 else
239 {
240 class_T *extends_cl = tv.vval.v_class;
241 ++extends_cl->class_refcount;
242 *extends_clp = extends_cl;
243 success = TRUE;
244 }
245 clear_tv(&tv);
246 }
247
248 return success;
249}
250
251/*
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200252 * Check whether a class/object member variable in "classmembers_gap" /
253 * "objmembers_gap" is a duplicate of a member in any of the extended parent
254 * class lineage. Returns TRUE if there are no duplicates.
255 */
256 static int
257validate_extends_members(
258 garray_T *classmembers_gap,
259 garray_T *objmembers_gap,
260 class_T *extends_cl)
261{
262 for (int loop = 1; loop <= 2; ++loop)
263 {
264 // loop == 1: check class members
265 // loop == 2: check object members
266 int member_count = loop == 1 ? classmembers_gap->ga_len
267 : objmembers_gap->ga_len;
268 if (member_count == 0)
269 continue;
270 ocmember_T *members = (ocmember_T *)(loop == 1
271 ? classmembers_gap->ga_data
272 : objmembers_gap->ga_data);
273
274 // Validate each member variable
275 for (int c_i = 0; c_i < member_count; c_i++)
276 {
277 class_T *p_cl = extends_cl;
278 ocmember_T *c_m = members + c_i;
279 char_u *pstr = (*c_m->ocm_name == '_')
280 ? c_m->ocm_name + 1 : c_m->ocm_name;
281
282 // Check in all the parent classes in the lineage
283 while (p_cl != NULL)
284 {
285 int p_member_count = loop == 1
286 ? p_cl->class_class_member_count
287 : p_cl->class_obj_member_count;
288 if (p_member_count == 0)
289 continue;
290 ocmember_T *p_members = (loop == 1
291 ? p_cl->class_class_members
292 : p_cl->class_obj_members);
293
294 // Compare against all the members in the parent class
295 for (int p_i = 0; p_i < p_member_count; p_i++)
296 {
297 ocmember_T *p_m = p_members + p_i;
298 char_u *qstr = (*p_m->ocm_name == '_')
299 ? p_m->ocm_name + 1 : p_m->ocm_name;
300 if (STRCMP(pstr, qstr) == 0)
301 {
302 semsg(_(e_duplicate_member_str), c_m->ocm_name);
303 return FALSE;
304 }
305 }
306
307 p_cl = p_cl->class_extends;
308 }
309 }
310 }
311
312 return TRUE;
313}
314
315/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200316 * Check the members of the interface class "ifcl" match the class members
317 * ("classmembers_gap") and object members ("objmembers_gap") of a class.
318 * Returns TRUE if the class and object member names are valid.
319 */
320 static int
321validate_interface_members(
322 char_u *intf_class_name,
323 class_T *ifcl,
324 garray_T *classmembers_gap,
325 garray_T *objmembers_gap)
326{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200327 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200328 {
329 // loop == 1: check class members
330 // loop == 2: check object members
331 int if_count = loop == 1 ? ifcl->class_class_member_count
332 : ifcl->class_obj_member_count;
333 if (if_count == 0)
334 continue;
335 ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
336 : ifcl->class_obj_members;
337 ocmember_T *cl_ms = (ocmember_T *)(loop == 1
338 ? classmembers_gap->ga_data
339 : objmembers_gap->ga_data);
340 int cl_count = loop == 1 ? classmembers_gap->ga_len
341 : objmembers_gap->ga_len;
342 for (int if_i = 0; if_i < if_count; ++if_i)
343 {
344 int cl_i;
345 for (cl_i = 0; cl_i < cl_count; ++cl_i)
346 {
347 ocmember_T *m = &cl_ms[cl_i];
348 where_T where = WHERE_INIT;
349
350 if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) != 0)
351 continue;
352
353 // Ensure the type is matching.
354 where.wt_func_name = (char *)m->ocm_name;
355 where.wt_kind = WT_MEMBER;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200356 if (check_type(if_ms[if_i].ocm_type, m->ocm_type, TRUE,
357 where) == FAIL)
358 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200359
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200360 if (if_ms[if_i].ocm_access != m->ocm_access)
361 {
362 semsg(_(e_member_str_of_interface_str_has_different_access),
363 if_ms[if_i].ocm_name, intf_class_name);
364 return FALSE;
365 }
366
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200367 break;
368 }
369 if (cl_i == cl_count)
370 {
371 semsg(_(e_member_str_of_interface_str_not_implemented),
372 if_ms[if_i].ocm_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200373 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200374 }
375 }
376 }
377
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200378 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200379}
380
381/*
382 * Check the functions/methods of the interface class "ifcl" match the class
383 * methods ("classfunctions_gap") and object functions ("objmemthods_gap") of a
384 * class.
385 * Returns TRUE if the class and object member names are valid.
386 */
387 static int
388validate_interface_methods(
389 char_u *intf_class_name,
390 class_T *ifcl,
391 garray_T *classfunctions_gap,
392 garray_T *objmethods_gap)
393{
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200394 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200395 {
396 // loop == 1: check class functions
397 // loop == 2: check object methods
398 int if_count = loop == 1 ? ifcl->class_class_function_count
399 : ifcl->class_obj_method_count;
400 if (if_count == 0)
401 continue;
402 ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
403 : ifcl->class_obj_methods;
404 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
405 ? classfunctions_gap->ga_data
406 : objmethods_gap->ga_data);
407 int cl_count = loop == 1 ? classfunctions_gap->ga_len
408 : objmethods_gap->ga_len;
409 for (int if_i = 0; if_i < if_count; ++if_i)
410 {
411 char_u *if_name = if_fp[if_i]->uf_name;
412 int cl_i;
413 for (cl_i = 0; cl_i < cl_count; ++cl_i)
414 {
415 char_u *cl_name = cl_fp[cl_i]->uf_name;
416 if (STRCMP(if_name, cl_name) == 0)
417 {
418 where_T where = WHERE_INIT;
419
420 // Ensure the type is matching.
421 where.wt_func_name = (char *)if_name;
422 where.wt_kind = WT_METHOD;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200423 if (check_type(if_fp[if_i]->uf_func_type,
424 cl_fp[cl_i]->uf_func_type, TRUE, where) == FAIL)
425 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200426 break;
427 }
428 }
429 if (cl_i == cl_count)
430 {
431 semsg(_(e_function_str_of_interface_str_not_implemented),
432 if_name, intf_class_name);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200433 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200434 }
435 }
436 }
437
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200438 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200439}
440
441/*
442 * Validate all the "implements" classes when creating a new class. The
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200443 * classes are returned in "intf_classes". The class functions, class members,
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200444 * object methods and object members in the new class are in
445 * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
446 * "objmembers_gap" respectively.
447 */
448 static int
449validate_implements_classes(
450 garray_T *impl_gap,
451 class_T **intf_classes,
452 garray_T *classfunctions_gap,
453 garray_T *classmembers_gap,
454 garray_T *objmethods_gap,
455 garray_T *objmembers_gap)
456{
457 int success = TRUE;
458
459 for (int i = 0; i < impl_gap->ga_len && success; ++i)
460 {
461 char_u *impl = ((char_u **)impl_gap->ga_data)[i];
462 typval_T tv;
463 tv.v_type = VAR_UNKNOWN;
464 if (eval_variable_import(impl, &tv) == FAIL)
465 {
466 semsg(_(e_interface_name_not_found_str), impl);
467 success = FALSE;
468 break;
469 }
470
471 if (tv.v_type != VAR_CLASS
472 || tv.vval.v_class == NULL
473 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
474 {
475 semsg(_(e_not_valid_interface_str), impl);
476 success = FALSE;
477 clear_tv(&tv);
478 break;
479 }
480
481 class_T *ifcl = tv.vval.v_class;
482 intf_classes[i] = ifcl;
483 ++ifcl->class_refcount;
484
485 // check the members of the interface match the members of the class
486 success = validate_interface_members(impl, ifcl, classmembers_gap,
487 objmembers_gap);
488
489 // check the functions/methods of the interface match the
490 // functions/methods of the class
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200491 if (success)
492 success = validate_interface_methods(impl, ifcl,
493 classfunctions_gap, objmethods_gap);
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200494 clear_tv(&tv);
495 }
496
497 return success;
498}
499
500/*
501 * Check no function argument name is used as a class member.
502 * (Object members are always accessed with "this." prefix, so no need
503 * to check them.)
504 */
505 static int
506check_func_arg_names(
507 garray_T *classfunctions_gap,
508 garray_T *objmethods_gap,
509 garray_T *classmembers_gap)
510{
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200511 // loop 1: class functions, loop 2: object methods
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200512 for (int loop = 1; loop <= 2; ++loop)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200513 {
514 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
515
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200516 for (int fi = 0; fi < gap->ga_len; ++fi)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200517 {
518 ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
519
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200520 for (int i = 0; i < uf->uf_args.ga_len; ++i)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200521 {
522 char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
523 garray_T *mgap = classmembers_gap;
524
525 // Check all the class member names
526 for (int mi = 0; mi < mgap->ga_len; ++mi)
527 {
528 char_u *mname = ((ocmember_T *)mgap->ga_data + mi)
529 ->ocm_name;
530 if (STRCMP(aname, mname) == 0)
531 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200532 if (uf->uf_script_ctx.sc_sid > 0)
533 SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
534
535 semsg(_(e_argument_already_declared_in_class_str),
536 aname);
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200537
538 return FALSE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200539 }
540 }
541 }
542 }
543 }
544
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +0200545 return TRUE;
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200546}
547
548/*
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +0200549 * Returns TRUE if the member "varname" is already defined.
550 */
551 static int
552is_duplicate_member(garray_T *mgap, char_u *varname, char_u *varname_end)
553{
554 char_u *pstr = (*varname == '_') ? varname + 1 : varname;
555
556 for (int i = 0; i < mgap->ga_len; ++i)
557 {
558 ocmember_T *m = ((ocmember_T *)mgap->ga_data) + i;
559 char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1 : m->ocm_name;
560 if (STRNCMP(pstr, qstr, varname_end - pstr) == 0)
561 {
562 char_u *name = vim_strnsave(varname, varname_end - varname);
563 semsg(_(e_duplicate_member_str), name);
564 vim_free(name);
565 return TRUE;
566 }
567 }
568
569 return FALSE;
570}
571
572/*
573 * Returns TRUE if the method "name" is already defined.
574 */
575 static int
576is_duplicate_method(garray_T *fgap, char_u *name)
577{
578 char_u *pstr = (*name == '_') ? name + 1 : name;
579
580 for (int i = 0; i < fgap->ga_len; ++i)
581 {
582 char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
583 char_u *qstr = *n == '_' ? n + 1 : n;
584 if (STRCMP(pstr, qstr) == 0)
585 {
586 semsg(_(e_duplicate_function_str), name);
587 return TRUE;
588 }
589 }
590
591 return FALSE;
592}
593
594/*
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +0200595 * Returns TRUE if the constructor is valid.
596 */
597 static int
598is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
599{
600 // Constructors are not allowed in abstract classes.
601 if (is_abstract)
602 {
603 emsg(_(e_cannot_define_new_function_in_abstract_class));
604 return FALSE;
605 }
606 // A constructor is always static, no need to define it so.
607 if (has_static)
608 {
609 emsg(_(e_cannot_define_new_function_as_static));
610 return FALSE;
611 }
612 // A return type should not be specified for the new()
613 // constructor method.
614 if (uf->uf_ret_type->tt_type != VAR_VOID)
615 {
616 emsg(_(e_cannot_use_a_return_type_with_new));
617 return FALSE;
618 }
619 return TRUE;
620}
621
622/*
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200623 * Update the interface class lookup table for the member index on the
624 * interface to the member index in the class implementing the interface.
625 * And a lookup table for the object method index on the interface
626 * to the object method index in the class implementing the interface.
627 * This is also used for updating the lookup table for the extended class
628 * hierarchy.
629 */
630 static int
631update_member_method_lookup_table(
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +0200632 class_T *ifcl,
633 class_T *cl,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +0200634 garray_T *objmethods,
635 int pobj_method_offset,
636 int is_interface)
Yegappan Lakshmananb1027282023-08-19 11:26:42 +0200637{
638 if (ifcl == NULL)
639 return OK;
640
641 // Table for members.
642 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
643 + ifcl->class_obj_member_count * sizeof(int));
644 if (if2cl == NULL)
645 return FAIL;
646 if2cl->i2c_next = ifcl->class_itf2class;
647 ifcl->class_itf2class = if2cl;
648 if2cl->i2c_class = cl;
649 if2cl->i2c_is_method = FALSE;
650
651 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
652 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
653 {
654 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
655 cl->class_obj_members[cl_i].ocm_name) == 0)
656 {
657 int *table = (int *)(if2cl + 1);
658 table[if_i] = cl_i;
659 break;
660 }
661 }
662
663 // Table for methods.
664 if2cl = alloc_clear(sizeof(itf2class_T)
665 + ifcl->class_obj_method_count * sizeof(int));
666 if (if2cl == NULL)
667 return FAIL;
668 if2cl->i2c_next = ifcl->class_itf2class;
669 ifcl->class_itf2class = if2cl;
670 if2cl->i2c_class = cl;
671 if2cl->i2c_is_method = TRUE;
672
673 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
674 {
675 int done = FALSE;
676 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
677 {
678 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
679 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name)
680 == 0)
681 {
682 int *table = (int *)(if2cl + 1);
683 table[if_i] = cl_i;
684 done = TRUE;
685 break;
686 }
687 }
688
689 // extended class object method is not overridden by the child class.
690 // Keep the method declared in one of the parent classes in the
691 // lineage.
692 if (!done && !is_interface)
693 {
694 // If "ifcl" is not the immediate parent of "cl", then search in
695 // the intermediate parent classes.
696 if (cl->class_extends != ifcl)
697 {
698 class_T *parent = cl->class_extends;
699 int method_offset = objmethods->ga_len;
700
701 while (!done && parent != NULL && parent != ifcl)
702 {
703
704 for (int cl_i = 0;
705 cl_i < parent->class_obj_method_count_child; ++cl_i)
706 {
707 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
708 parent->class_obj_methods[cl_i]->uf_name)
709 == 0)
710 {
711 int *table = (int *)(if2cl + 1);
712 table[if_i] = method_offset + cl_i;
713 done = TRUE;
714 break;
715 }
716 }
717 method_offset += parent->class_obj_method_count_child;
718 parent = parent->class_extends;
719 }
720 }
721
722 if (!done)
723 {
724 int *table = (int *)(if2cl + 1);
725 table[if_i] = pobj_method_offset + if_i;
726 }
727 }
728 }
729
730 return OK;
731}
732
733/*
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200734 * Update the member and object method lookup tables for a new class in the
735 * interface class.
736 * For each interface add a lookup table for the member index on the interface
737 * to the member index in the new class. And a lookup table for the object
738 * method index on the interface to the object method index in the new class.
739 */
740 static int
741add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
742{
743 for (int i = 0; i < cl->class_interface_count; ++i)
744 {
745 class_T *ifcl = cl->class_interfaces_cl[i];
746
747 if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
748 0, TRUE) == FAIL)
749 return FAIL;
750 }
751
752 // Update the lookup table for the extended class, if nay
753 if (extends_cl != NULL)
754 {
755 class_T *pclass = extends_cl;
756 int pobj_method_offset = objmethods_gap->ga_len;
757
758 // Update the entire lineage of extended classes.
759 while (pclass != NULL)
760 {
761 if (update_member_method_lookup_table(pclass, cl,
762 objmethods_gap, pobj_method_offset, FALSE) == FAIL)
763 return FAIL;
764
765 pobj_method_offset += pclass->class_obj_method_count_child;
766 pclass = pclass->class_extends;
767 }
768 }
769
770 return OK;
771}
772
773/*
774 * Add class members to a new class. Allocate a typval for each class member
775 * and initialize it.
776 */
777 static void
778add_class_members(class_T *cl, exarg_T *eap)
779{
780 // Allocate a typval for each class member and initialize it.
781 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
782 cl->class_class_member_count);
783 if (cl->class_members_tv == NULL)
784 return;
785
786 for (int i = 0; i < cl->class_class_member_count; ++i)
787 {
788 ocmember_T *m = &cl->class_class_members[i];
789 typval_T *tv = &cl->class_members_tv[i];
790 if (m->ocm_init != NULL)
791 {
792 typval_T *etv = eval_expr(m->ocm_init, eap);
793 if (etv != NULL)
794 {
795 *tv = *etv;
796 vim_free(etv);
797 }
798 }
799 else
800 {
801 // TODO: proper default value
802 tv->v_type = m->ocm_type->tt_type;
803 tv->vval.v_string = NULL;
804 }
805 }
806}
807
808/*
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +0200809 * Add a default constructor method (new()) to the class "cl".
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +0200810 */
811 static void
812add_default_constructor(
813 class_T *cl,
814 garray_T *classfunctions_gap,
815 garray_T *type_list_gap)
816{
817 garray_T fga;
818
819 ga_init2(&fga, 1, 1000);
820 ga_concat(&fga, (char_u *)"new(");
821 for (int i = 0; i < cl->class_obj_member_count; ++i)
822 {
823 if (i > 0)
824 ga_concat(&fga, (char_u *)", ");
825 ga_concat(&fga, (char_u *)"this.");
826 ocmember_T *m = cl->class_obj_members + i;
827 ga_concat(&fga, (char_u *)m->ocm_name);
828 ga_concat(&fga, (char_u *)" = v:none");
829 }
830 ga_concat(&fga, (char_u *)")\nenddef\n");
831 ga_append(&fga, NUL);
832
833 exarg_T fea;
834 CLEAR_FIELD(fea);
835 fea.cmdidx = CMD_def;
836 fea.cmd = fea.arg = fga.ga_data;
837
838 garray_T lines_to_free;
839 ga_init2(&lines_to_free, sizeof(char_u *), 50);
840
841 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS);
842
843 ga_clear_strings(&lines_to_free);
844 vim_free(fga.ga_data);
845
846 if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
847 {
848 ((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
849 = nf;
850 ++classfunctions_gap->ga_len;
851
852 nf->uf_flags |= FC_NEW;
853 nf->uf_ret_type = get_type_ptr(type_list_gap);
854 if (nf->uf_ret_type != NULL)
855 {
856 nf->uf_ret_type->tt_type = VAR_OBJECT;
857 nf->uf_ret_type->tt_class = cl;
858 nf->uf_ret_type->tt_argcount = 0;
859 nf->uf_ret_type->tt_args = NULL;
860 }
861 }
862}
863
864/*
865 * Add the class functions and object methods to the new class "cl".
866 * When extending a class, add the functions and methods from the parent class
867 * also.
868 */
869 static int
870add_classfuncs_objmethods(
871 class_T *cl,
872 class_T *extends_cl,
873 garray_T *classfunctions_gap,
874 garray_T *objmethods_gap)
875{
876 // loop 1: class functions, loop 2: object methods
877 for (int loop = 1; loop <= 2; ++loop)
878 {
879 garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
880 int *fcount = loop == 1 ? &cl->class_class_function_count
881 : &cl->class_obj_method_count;
882 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
883 : &cl->class_obj_methods;
884
885 int parent_count = 0;
886 if (extends_cl != NULL)
887 // Include functions from the parent.
888 parent_count = loop == 1
889 ? extends_cl->class_class_function_count
890 : extends_cl->class_obj_method_count;
891
892 *fcount = parent_count + gap->ga_len;
893 if (*fcount == 0)
894 {
895 *fup = NULL;
896 continue;
897 }
898 *fup = ALLOC_MULT(ufunc_T *, *fcount);
899 if (*fup == NULL)
900 return FAIL;
901
902 if (gap->ga_len != 0)
903 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
904 vim_free(gap->ga_data);
905 if (loop == 1)
906 cl->class_class_function_count_child = gap->ga_len;
907 else
908 cl->class_obj_method_count_child = gap->ga_len;
909
910 int skipped = 0;
911 for (int i = 0; i < parent_count; ++i)
912 {
913 // Copy functions from the parent. Can't use the same
914 // function, because "uf_class" is different and compilation
915 // will have a different result.
916 // Put them after the functions in the current class, object
917 // methods may be overruled, then "super.Method()" is used to
918 // find a method from the parent.
919 // Skip "new" functions. TODO: not all of them.
920 if (loop == 1 && STRNCMP(
921 extends_cl->class_class_functions[i]->uf_name,
922 "new", 3) == 0)
923 ++skipped;
924 else
925 {
926 ufunc_T *pf = (loop == 1
927 ? extends_cl->class_class_functions
928 : extends_cl->class_obj_methods)[i];
929 (*fup)[gap->ga_len + i - skipped] = copy_function(pf);
930
931 // If the child class overrides a function from the parent
932 // the signature must be equal.
933 char_u *pname = pf->uf_name;
934 for (int ci = 0; ci < gap->ga_len; ++ci)
935 {
936 ufunc_T *cf = (*fup)[ci];
937 char_u *cname = cf->uf_name;
938 if (STRCMP(pname, cname) == 0)
939 {
940 where_T where = WHERE_INIT;
941 where.wt_func_name = (char *)pname;
942 where.wt_kind = WT_METHOD;
943 (void)check_type(pf->uf_func_type, cf->uf_func_type,
944 TRUE, where);
945 }
946 }
947 }
948 }
949
950 *fcount -= skipped;
951
952 // Set the class pointer on all the functions and object methods.
953 for (int i = 0; i < *fcount; ++i)
954 {
955 ufunc_T *fp = (*fup)[i];
956 fp->uf_class = cl;
957 if (loop == 2)
958 fp->uf_flags |= FC_OBJECT;
959 }
960 }
961
962 return OK;
963}
964
965/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000966 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +0000967 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000968 */
969 void
970ex_class(exarg_T *eap)
971{
Bram Moolenaar83ae6152023-02-25 19:59:31 +0000972 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
973 long start_lnum = SOURCING_LNUM;
Bram Moolenaar554d0312023-01-05 19:59:18 +0000974
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000975 char_u *arg = eap->arg;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000976 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000977 if (is_abstract)
978 {
979 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
980 {
981 semsg(_(e_invalid_argument_str), arg);
982 return;
983 }
984 arg = skipwhite(arg + 5);
Bram Moolenaar24a8d062023-01-14 13:12:06 +0000985 is_class = TRUE;
986 }
987
988 if (!current_script_is_vim9()
989 || (cmdmod.cmod_flags & CMOD_LEGACY)
990 || !getline_equal(eap->getline, eap->cookie, getsourceline))
991 {
992 if (is_class)
993 emsg(_(e_class_can_only_be_defined_in_vim9_script));
994 else
995 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
996 return;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000997 }
998
999 if (!ASCII_ISUPPER(*arg))
1000 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001001 if (is_class)
1002 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
1003 else
1004 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
1005 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001006 return;
1007 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001008 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1009 if (!IS_WHITE_OR_NUL(*name_end))
1010 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001011 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001012 return;
1013 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001014 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001015
Bram Moolenaara86655a2023-01-12 17:06:27 +00001016 // "export class" gets used when creating the class, don't use "is_export"
1017 // for the items inside the class.
1018 int class_export = is_export;
1019 is_export = FALSE;
1020
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001021 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001022 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001023
Bram Moolenaar83677162023-01-08 19:54:10 +00001024 // Name for "extends BaseClass"
1025 char_u *extends = NULL;
1026
Bram Moolenaar94674f22023-01-06 18:42:20 +00001027 // Names for "implements SomeInterface"
1028 garray_T ga_impl;
1029 ga_init2(&ga_impl, sizeof(char_u *), 5);
1030
1031 arg = skipwhite(name_end);
1032 while (*arg != NUL && *arg != '#' && *arg != '\n')
1033 {
1034 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +00001035 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +00001036 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
1037 {
1038 if (extends != NULL)
1039 {
1040 emsg(_(e_duplicate_extends));
1041 goto early_ret;
1042 }
1043 arg = skipwhite(arg + 7);
1044 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
1045 if (!IS_WHITE_OR_NUL(*end))
1046 {
1047 semsg(_(e_white_space_required_after_name_str), arg);
1048 goto early_ret;
1049 }
1050 extends = vim_strnsave(arg, end - arg);
1051 if (extends == NULL)
1052 goto early_ret;
1053
1054 arg = skipwhite(end + 1);
1055 }
1056 else if (STRNCMP(arg, "implements", 10) == 0
1057 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +00001058 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001059 if (ga_impl.ga_len > 0)
1060 {
1061 emsg(_(e_duplicate_implements));
1062 goto early_ret;
1063 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001064 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001065
1066 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +00001067 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +00001068 char_u *impl_end = find_name_end(arg, NULL, NULL,
1069 FNE_CHECK_START);
1070 if (!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
1071 {
1072 semsg(_(e_white_space_required_after_name_str), arg);
1073 goto early_ret;
1074 }
1075 char_u *iname = vim_strnsave(arg, impl_end - arg);
1076 if (iname == NULL)
1077 goto early_ret;
1078 for (int i = 0; i < ga_impl.ga_len; ++i)
1079 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
1080 {
1081 semsg(_(e_duplicate_interface_after_implements_str),
1082 iname);
1083 vim_free(iname);
1084 goto early_ret;
1085 }
1086 if (ga_add_string(&ga_impl, iname) == FAIL)
1087 {
1088 vim_free(iname);
1089 goto early_ret;
1090 }
1091 if (*impl_end != ',')
1092 {
1093 arg = skipwhite(impl_end);
1094 break;
1095 }
1096 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001097 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001098 }
1099 else
1100 {
1101 semsg(_(e_trailing_characters_str), arg);
1102early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +00001103 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001104 ga_clear_strings(&ga_impl);
1105 return;
1106 }
1107 }
1108
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001109 garray_T type_list; // list of pointers to allocated types
1110 ga_init2(&type_list, sizeof(type_T *), 10);
1111
Bram Moolenaard505d172022-12-18 21:42:55 +00001112 // Growarray with class members declared in the class.
1113 garray_T classmembers;
1114 ga_init2(&classmembers, sizeof(ocmember_T), 10);
1115
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001116 // Growarray with functions declared in the class.
1117 garray_T classfunctions;
1118 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +00001119
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001120 // Growarray with object members declared in the class.
1121 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +00001122 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001123
1124 // Growarray with object methods declared in the class.
1125 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001126 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001127
1128 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +00001129 * Go over the body of the class/interface until "endclass" or
1130 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001131 */
1132 char_u *theline = NULL;
1133 int success = FALSE;
1134 for (;;)
1135 {
1136 vim_free(theline);
1137 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
1138 if (theline == NULL)
1139 break;
1140 char_u *line = skipwhite(theline);
1141
Bram Moolenaar418b5472022-12-20 13:38:22 +00001142 // Skip empty and comment lines.
1143 if (*line == NUL)
1144 continue;
1145 if (*line == '#')
1146 {
1147 if (vim9_bad_comment(line))
1148 break;
1149 continue;
1150 }
1151
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001152 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001153 char *end_name = is_class ? "endclass" : "endinterface";
1154 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001155 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001156 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001157 semsg(_(e_command_cannot_be_shortened_str), line);
1158 else if (*p == '|' || !ends_excmd2(line, p))
1159 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +00001160 else
1161 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001162 break;
1163 }
Bram Moolenaar554d0312023-01-05 19:59:18 +00001164 char *wrong_name = is_class ? "endinterface" : "endclass";
1165 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
1166 {
Bram Moolenaar657aea72023-01-27 13:16:19 +00001167 semsg(_(e_invalid_command_str_expected_str), line, end_name);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001168 break;
1169 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001170
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001171 int has_public = FALSE;
1172 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001173 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001174 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001175 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001176 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001177 break;
1178 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001179 has_public = TRUE;
1180 p = skipwhite(line + 6);
1181
Bram Moolenaard505d172022-12-18 21:42:55 +00001182 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001183 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001184 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001185 break;
1186 }
1187 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001188
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001189 int has_static = FALSE;
1190 char_u *ps = p;
1191 if (checkforcmd(&p, "static", 4))
1192 {
1193 if (STRNCMP(ps, "static", 6) != 0)
1194 {
1195 semsg(_(e_command_cannot_be_shortened_str), ps);
1196 break;
1197 }
1198 has_static = TRUE;
1199 p = skipwhite(ps + 6);
1200 }
1201
Bram Moolenaard505d172022-12-18 21:42:55 +00001202 // object members (public, read access, private):
1203 // "this._varname"
1204 // "this.varname"
1205 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001206 if (STRNCMP(p, "this", 4) == 0)
1207 {
1208 if (p[4] != '.' || !eval_isnamec1(p[5]))
1209 {
1210 semsg(_(e_invalid_object_member_declaration_str), p);
1211 break;
1212 }
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001213 if (has_static)
1214 {
1215 emsg(_(e_static_cannot_be_followed_by_this));
1216 break;
1217 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001218 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +00001219 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +00001220 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +00001221 char_u *init_expr = NULL;
1222 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001223 &varname_end, &type_list, &type,
1224 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001225 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001226 if (is_duplicate_member(&objmembers, varname, varname_end))
1227 {
1228 vim_free(init_expr);
1229 break;
1230 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001231 if (add_member(&objmembers, varname, varname_end,
1232 has_public, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +00001233 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001234 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001235 break;
1236 }
Bram Moolenaard505d172022-12-18 21:42:55 +00001237 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001238
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001239 // constructors:
1240 // def new()
1241 // enddef
1242 // def newOther()
1243 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001244 // object methods and class functions:
1245 // def SomeMethod()
1246 // enddef
1247 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001248 // enddef
1249 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001250 // def <Tval> someMethod()
1251 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001252 else if (checkforcmd(&p, "def", 3))
1253 {
1254 exarg_T ea;
1255 garray_T lines_to_free;
1256
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001257 // TODO: error for "public static def Func()"?
1258
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001259 CLEAR_FIELD(ea);
1260 ea.cmd = line;
1261 ea.arg = p;
1262 ea.cmdidx = CMD_def;
1263 ea.getline = eap->getline;
1264 ea.cookie = eap->cookie;
1265
1266 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Bram Moolenaar554d0312023-01-05 19:59:18 +00001267 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
1268 is_class ? CF_CLASS : CF_INTERFACE);
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001269 ga_clear_strings(&lines_to_free);
1270
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001271 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001272 {
Bram Moolenaar58b40092023-01-11 15:59:05 +00001273 char_u *name = uf->uf_name;
1274 int is_new = STRNCMP(name, "new", 3) == 0;
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001275
1276 if (is_new && !is_valid_constructor(uf, is_abstract, has_static))
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001277 {
Yegappan Lakshmananb1027282023-08-19 11:26:42 +02001278 func_clear_free(uf, FALSE);
Bram Moolenaar24a8d062023-01-14 13:12:06 +00001279 break;
1280 }
Gianmaria Bajo4b9777a2023-08-29 22:26:30 +02001281
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001282 garray_T *fgap = has_static || is_new
1283 ? &classfunctions : &objmethods;
Bram Moolenaar58b40092023-01-11 15:59:05 +00001284 // Check the name isn't used already.
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001285 if (is_duplicate_method(fgap, name))
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001286 {
1287 success = FALSE;
1288 func_clear_free(uf, FALSE);
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001289 break;
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001290 }
Bram Moolenaar58b40092023-01-11 15:59:05 +00001291
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001292 if (ga_grow(fgap, 1) == OK)
1293 {
1294 if (is_new)
1295 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001296
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001297 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
1298 ++fgap->ga_len;
1299 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001300 }
1301 }
1302
1303 // class members
1304 else if (has_static)
1305 {
1306 // class members (public, read access, private):
1307 // "static _varname"
1308 // "static varname"
1309 // "public static varname"
1310 char_u *varname = p;
1311 char_u *varname_end = NULL;
1312 type_T *type = NULL;
1313 char_u *init_expr = NULL;
1314 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +00001315 &varname_end, &type_list, &type,
1316 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001317 break;
Yegappan Lakshmanan2ba9d2e2023-08-28 21:26:23 +02001318 if (is_duplicate_member(&classmembers, varname, varname_end))
1319 {
1320 vim_free(init_expr);
1321 break;
1322 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001323 if (add_member(&classmembers, varname, varname_end,
1324 has_public, type, init_expr) == FAIL)
1325 {
1326 vim_free(init_expr);
1327 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001328 }
1329 }
1330
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001331 else
1332 {
Bram Moolenaar554d0312023-01-05 19:59:18 +00001333 if (is_class)
1334 semsg(_(e_not_valid_command_in_class_str), line);
1335 else
1336 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001337 break;
1338 }
1339 }
1340 vim_free(theline);
1341
Bram Moolenaar83677162023-01-08 19:54:10 +00001342 class_T *extends_cl = NULL; // class from "extends" argument
1343
1344 /*
1345 * Check a few things before defining the class.
1346 */
1347
1348 // Check the "extends" class is valid.
1349 if (success && extends != NULL)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001350 success = validate_extends_class(extends, &extends_cl);
Bram Moolenaar83677162023-01-08 19:54:10 +00001351 VIM_CLEAR(extends);
1352
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001353 // Check the new class members and object members doesn't duplicate the
1354 // members in the extended class lineage.
1355 if (success && extends_cl != NULL)
1356 success = validate_extends_members(&classmembers, &objmembers,
1357 extends_cl);
1358
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001359 class_T **intf_classes = NULL;
1360
Bram Moolenaar83677162023-01-08 19:54:10 +00001361 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001362 if (success && ga_impl.ga_len > 0)
1363 {
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001364 intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
1365
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001366 success = validate_implements_classes(&ga_impl, intf_classes,
1367 &classfunctions, &classmembers,
1368 &objmethods, &objmembers);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001369 }
1370
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001371 // Check no function argument name is used as a class member.
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001372 if (success)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001373 success = check_func_arg_names(&classfunctions, &objmethods,
1374 &classmembers);
Bram Moolenaard40f00c2023-01-13 17:36:49 +00001375
Bram Moolenaareb533502022-12-14 15:06:11 +00001376 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001377 if (success)
1378 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001379 // "endclass" encountered without failures: Create the class.
1380
Bram Moolenaareb533502022-12-14 15:06:11 +00001381 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001382 if (cl == NULL)
1383 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +00001384 if (!is_class)
1385 cl->class_flags = CLASS_INTERFACE;
1386
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001387 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +00001388 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +00001389 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001390 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +00001391
Bram Moolenaard0200c82023-01-28 15:19:40 +00001392 if (extends_cl != NULL)
1393 {
1394 cl->class_extends = extends_cl;
1395 extends_cl->class_flags |= CLASS_EXTENDED;
1396 }
Bram Moolenaar83677162023-01-08 19:54:10 +00001397
Bram Moolenaard505d172022-12-18 21:42:55 +00001398 // Add class and object members to "cl".
1399 if (add_members_to_class(&classmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001400 extends_cl == NULL ? NULL
1401 : extends_cl->class_class_members,
1402 extends_cl == NULL ? 0
1403 : extends_cl->class_class_member_count,
1404 &cl->class_class_members,
1405 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +00001406 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +00001407 extends_cl == NULL ? NULL
1408 : extends_cl->class_obj_members,
1409 extends_cl == NULL ? 0
1410 : extends_cl->class_obj_member_count,
1411 &cl->class_obj_members,
1412 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +00001413 goto cleanup;
1414
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001415 if (ga_impl.ga_len > 0)
1416 {
1417 // Move the "implements" names into the class.
1418 cl->class_interface_count = ga_impl.ga_len;
1419 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
1420 if (cl->class_interfaces == NULL)
1421 goto cleanup;
1422 for (int i = 0; i < ga_impl.ga_len; ++i)
1423 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
1424 VIM_CLEAR(ga_impl.ga_data);
1425 ga_impl.ga_len = 0;
1426
Bram Moolenaard0200c82023-01-28 15:19:40 +00001427 cl->class_interfaces_cl = intf_classes;
1428 intf_classes = NULL;
1429 }
1430
1431 if (cl->class_interface_count > 0 || extends_cl != NULL)
1432 {
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001433 // Add a method and member lookup table to each of the interface
1434 // classes.
1435 if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
1436 goto cleanup;
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001437 }
1438
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001439 // Allocate a typval for each class member and initialize it.
Bram Moolenaar554d0312023-01-05 19:59:18 +00001440 if (is_class && cl->class_class_member_count > 0)
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001441 add_class_members(cl, eap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001442
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001443 int have_new = FALSE;
1444 ufunc_T *class_func = NULL;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001445 for (int i = 0; i < classfunctions.ga_len; ++i)
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001446 {
1447 class_func = ((ufunc_T **)classfunctions.ga_data)[i];
1448 if (STRCMP(class_func->uf_name, "new") == 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001449 {
1450 have_new = TRUE;
1451 break;
1452 }
Yegappan Lakshmanan6ac15442023-08-20 18:20:17 +02001453 }
1454
1455 if (have_new)
1456 // The return type of new() is an object of class "cl"
1457 class_func->uf_ret_type->tt_class = cl;
1458 else if (is_class && !is_abstract && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001459 // No new() method was defined, add the default constructor.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001460 add_default_constructor(cl, &classfunctions, &type_list);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001461
Bram Moolenaar58b40092023-01-11 15:59:05 +00001462 // Move all the functions into the created class.
Yegappan Lakshmanan4b1cc792023-08-19 22:39:33 +02001463 if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
1464 &objmethods) == FAIL)
1465 goto cleanup;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001466
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001467 cl->class_type.tt_type = VAR_CLASS;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001468 cl->class_type.tt_class = cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001469 cl->class_object_type.tt_type = VAR_OBJECT;
Bram Moolenaarb1e32ac2023-02-21 12:38:51 +00001470 cl->class_object_type.tt_class = cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001471 cl->class_type_list = type_list;
1472
1473 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +00001474 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001475
1476 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +00001477 // TODO: handle other context, e.g. in a function
Ernie Rael21d32122023-09-02 15:09:18 +02001478 // TODO: does uf_hash need to be cleared?
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001479 typval_T tv;
1480 tv.v_type = VAR_CLASS;
1481 tv.vval.v_class = cl;
Bram Moolenaara86655a2023-01-12 17:06:27 +00001482 is_export = class_export;
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001483 SOURCING_LNUM = start_lnum;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001484 set_var_const(cl->class_name, current_sctx.sc_sid,
Bram Moolenaar83ae6152023-02-25 19:59:31 +00001485 NULL, &tv, FALSE, 0, 0);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001486 return;
1487 }
1488
1489cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +00001490 if (cl != NULL)
1491 {
1492 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001493 vim_free(cl->class_class_functions);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001494 if (cl->class_interfaces != NULL)
1495 {
1496 for (int i = 0; i < cl->class_interface_count; ++i)
1497 vim_free(cl->class_interfaces[i]);
1498 vim_free(cl->class_interfaces);
1499 }
1500 if (cl->class_interfaces_cl != NULL)
1501 {
1502 for (int i = 0; i < cl->class_interface_count; ++i)
1503 class_unref(cl->class_interfaces_cl[i]);
1504 vim_free(cl->class_interfaces_cl);
1505 }
Bram Moolenaareb533502022-12-14 15:06:11 +00001506 vim_free(cl->class_obj_members);
1507 vim_free(cl->class_obj_methods);
1508 vim_free(cl);
1509 }
1510
Bram Moolenaar83677162023-01-08 19:54:10 +00001511 vim_free(extends);
1512 class_unref(extends_cl);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001513
1514 if (intf_classes != NULL)
1515 {
1516 for (int i = 0; i < ga_impl.ga_len; ++i)
1517 class_unref(intf_classes[i]);
1518 vim_free(intf_classes);
1519 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001520 ga_clear_strings(&ga_impl);
1521
Bram Moolenaard505d172022-12-18 21:42:55 +00001522 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001523 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001524 garray_T *gap = round == 1 ? &classmembers : &objmembers;
1525 if (gap->ga_len == 0 || gap->ga_data == NULL)
1526 continue;
1527
1528 for (int i = 0; i < gap->ga_len; ++i)
1529 {
1530 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
1531 vim_free(m->ocm_name);
1532 vim_free(m->ocm_init);
1533 }
1534 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001535 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001536
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001537 for (int i = 0; i < objmethods.ga_len; ++i)
1538 {
1539 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
1540 func_clear_free(uf, FALSE);
1541 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001542 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001543
1544 for (int i = 0; i < classfunctions.ga_len; ++i)
1545 {
1546 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
1547 func_clear_free(uf, FALSE);
1548 }
1549 ga_clear(&classfunctions);
1550
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001551 clear_type_list(&type_list);
1552}
1553
1554/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001555 * Find member "name" in class "cl", set "member_idx" to the member index and
1556 * return its type.
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001557 * When "is_object" is TRUE, then look for object members. Otherwise look for
1558 * class members.
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001559 * When not found "member_idx" is set to -1 and t_any is returned.
Ernie Rael456ae552023-09-01 18:54:54 +02001560 * Set *p_m ocmmember_T if not NULL
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001561 */
1562 type_T *
1563class_member_type(
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001564 class_T *cl,
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001565 int is_object,
Yegappan Lakshmananeb91e242023-08-31 18:10:46 +02001566 char_u *name,
1567 char_u *name_end,
1568 int *member_idx,
Ernie Rael456ae552023-09-01 18:54:54 +02001569 ocmember_T **p_m)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001570{
1571 *member_idx = -1; // not found (yet)
1572 size_t len = name_end - name;
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001573 int member_count = is_object ? cl->class_obj_member_count
1574 : cl->class_class_member_count;
1575 ocmember_T *members = is_object ? cl->class_obj_members
1576 : cl->class_class_members;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001577
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001578 for (int i = 0; i < member_count; ++i)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001579 {
Yegappan Lakshmanan3775f772023-09-01 22:05:45 +02001580 ocmember_T *m = members + i;
Bram Moolenaard505d172022-12-18 21:42:55 +00001581 if (STRNCMP(m->ocm_name, name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001582 {
1583 *member_idx = i;
Ernie Rael456ae552023-09-01 18:54:54 +02001584 if (p_m != NULL)
1585 *p_m = m;
Bram Moolenaard505d172022-12-18 21:42:55 +00001586 return m->ocm_type;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001587 }
1588 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +00001589
1590 semsg(_(e_unknown_variable_str), name);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001591 return &t_any;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001592}
1593
1594/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001595 * Handle ":enum" up to ":endenum".
1596 */
1597 void
1598ex_enum(exarg_T *eap UNUSED)
1599{
1600 // TODO
1601}
1602
1603/*
1604 * Handle ":type".
1605 */
1606 void
1607ex_type(exarg_T *eap UNUSED)
1608{
1609 // TODO
1610}
1611
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001612/*
1613 * Evaluate what comes after a class:
1614 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001615 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001616 * - class constructor: SomeClass.new()
1617 * - object member: someObject.varname
1618 * - object method: someObject.SomeMethod()
1619 *
1620 * "*arg" points to the '.'.
1621 * "*arg" is advanced to after the member name or method call.
1622 *
1623 * Returns FAIL or OK.
1624 */
1625 int
1626class_object_index(
1627 char_u **arg,
1628 typval_T *rettv,
1629 evalarg_T *evalarg,
1630 int verbose UNUSED) // give error messages
1631{
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001632 if (VIM_ISWHITE((*arg)[1]))
1633 {
1634 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
1635 return FAIL;
1636 }
1637
1638 ++*arg;
1639 char_u *name = *arg;
1640 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
1641 if (name_end == name)
1642 return FAIL;
1643 size_t len = name_end - name;
1644
Bram Moolenaar552bdca2023-02-17 21:08:50 +00001645 class_T *cl;
1646 if (rettv->v_type == VAR_CLASS)
1647 cl = rettv->vval.v_class;
1648 else // VAR_OBJECT
1649 {
1650 if (rettv->vval.v_object == NULL)
1651 {
1652 emsg(_(e_using_null_object));
1653 return FAIL;
1654 }
1655 cl = rettv->vval.v_object->obj_class;
1656 }
1657
Bram Moolenaard13dd302023-03-11 20:56:35 +00001658 if (cl == NULL)
1659 {
1660 emsg(_(e_incomplete_type));
1661 return FAIL;
1662 }
1663
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001664 if (*name_end == '(')
1665 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001666 int on_class = rettv->v_type == VAR_CLASS;
1667 int count = on_class ? cl->class_class_function_count
1668 : cl->class_obj_method_count;
1669 for (int i = 0; i < count; ++i)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001670 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001671 ufunc_T *fp = on_class ? cl->class_class_functions[i]
1672 : cl->class_obj_methods[i];
Bram Moolenaar4ae00572022-12-09 22:49:23 +00001673 // Use a separate pointer to avoid that ASAN complains about
1674 // uf_name[] only being 4 characters.
1675 char_u *ufname = (char_u *)fp->uf_name;
1676 if (STRNCMP(name, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001677 {
1678 typval_T argvars[MAX_FUNC_ARGS + 1];
1679 int argcount = 0;
1680
Yegappan Lakshmanane3b6c782023-08-29 22:32:02 +02001681 if (*ufname == '_')
Yegappan Lakshmanancd7293b2023-08-27 19:18:23 +02001682 {
1683 // Cannot access a private method outside of a class
1684 semsg(_(e_cannot_access_private_method_str), name);
1685 return FAIL;
1686 }
1687
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001688 char_u *argp = name_end;
1689 int ret = get_func_arguments(&argp, evalarg, 0,
1690 argvars, &argcount);
1691 if (ret == FAIL)
1692 return FAIL;
1693
1694 funcexe_T funcexe;
1695 CLEAR_FIELD(funcexe);
1696 funcexe.fe_evaluate = TRUE;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001697 if (rettv->v_type == VAR_OBJECT)
1698 {
1699 funcexe.fe_object = rettv->vval.v_object;
1700 ++funcexe.fe_object->obj_refcount;
1701 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001702
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001703 // Clear the class or object after calling the function, in
1704 // case the refcount is one.
1705 typval_T tv_tofree = *rettv;
1706 rettv->v_type = VAR_UNKNOWN;
1707
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001708 // Call the user function. Result goes into rettv;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001709 int error = call_user_func_check(fp, argcount, argvars,
1710 rettv, &funcexe, NULL);
1711
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001712 // Clear the previous rettv and the arguments.
1713 clear_tv(&tv_tofree);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001714 for (int idx = 0; idx < argcount; ++idx)
1715 clear_tv(&argvars[idx]);
1716
1717 if (error != FCERR_NONE)
1718 {
1719 user_func_error(error, printable_func_name(fp),
1720 funcexe.fe_found_var);
1721 return FAIL;
1722 }
1723 *arg = argp;
1724 return OK;
1725 }
1726 }
1727
1728 semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name);
1729 }
1730
1731 else if (rettv->v_type == VAR_OBJECT)
1732 {
1733 for (int i = 0; i < cl->class_obj_member_count; ++i)
1734 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001735 ocmember_T *m = &cl->class_obj_members[i];
1736 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001737 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001738 if (*name == '_')
1739 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001740 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001741 return FAIL;
1742 }
1743
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001744 // The object only contains a pointer to the class, the member
1745 // values array follows right after that.
1746 object_T *obj = rettv->vval.v_object;
1747 typval_T *tv = (typval_T *)(obj + 1) + i;
1748 copy_tv(tv, rettv);
1749 object_unref(obj);
1750
1751 *arg = name_end;
1752 return OK;
1753 }
1754 }
1755
1756 semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
1757 }
1758
Bram Moolenaard505d172022-12-18 21:42:55 +00001759 else if (rettv->v_type == VAR_CLASS)
1760 {
1761 // class member
1762 for (int i = 0; i < cl->class_class_member_count; ++i)
1763 {
1764 ocmember_T *m = &cl->class_class_members[i];
1765 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
1766 {
1767 if (*name == '_')
1768 {
1769 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
1770 return FAIL;
1771 }
1772
1773 typval_T *tv = &cl->class_members_tv[i];
1774 copy_tv(tv, rettv);
1775 class_unref(cl);
1776
1777 *arg = name_end;
1778 return OK;
1779 }
1780 }
1781
1782 semsg(_(e_member_not_found_on_class_str_str), cl->class_name, name);
1783 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001784
1785 return FAIL;
1786}
1787
1788/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001789 * If "arg" points to a class or object method, return it.
1790 * Otherwise return NULL.
1791 */
1792 ufunc_T *
1793find_class_func(char_u **arg)
1794{
1795 char_u *name = *arg;
1796 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
1797 if (name_end == name || *name_end != '.')
1798 return NULL;
1799
1800 size_t len = name_end - name;
1801 typval_T tv;
1802 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00001803 if (eval_variable(name, (int)len,
1804 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001805 return NULL;
1806 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00001807 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001808
1809 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
1810 : tv.vval.v_object->obj_class;
1811 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00001812 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001813 char_u *fname = name_end + 1;
1814 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
1815 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00001816 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001817 len = fname_end - fname;
1818
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001819 int count = tv.v_type == VAR_CLASS ? cl->class_class_function_count
1820 : cl->class_obj_method_count;
1821 ufunc_T **funcs = tv.v_type == VAR_CLASS ? cl->class_class_functions
1822 : cl->class_obj_methods;
1823 for (int i = 0; i < count; ++i)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001824 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001825 ufunc_T *fp = funcs[i];
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001826 // Use a separate pointer to avoid that ASAN complains about
1827 // uf_name[] only being 4 characters.
1828 char_u *ufname = (char_u *)fp->uf_name;
1829 if (STRNCMP(fname, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaareb533502022-12-14 15:06:11 +00001830 {
1831 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001832 return fp;
Bram Moolenaareb533502022-12-14 15:06:11 +00001833 }
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001834 }
1835
Bram Moolenaareb533502022-12-14 15:06:11 +00001836fail_after_eval:
1837 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001838 return NULL;
1839}
1840
1841/*
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001842 * If "name[len]" is a class member in cctx->ctx_ufunc->uf_class return the
1843 * index in class.class_class_members[].
1844 * If "cl_ret" is not NULL set it to the class.
1845 * Otherwise return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001846 */
1847 int
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001848class_member_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001849{
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001850 if (cctx == NULL || cctx->ctx_ufunc == NULL
1851 || cctx->ctx_ufunc->uf_class == NULL)
1852 return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001853 class_T *cl = cctx->ctx_ufunc->uf_class;
1854
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001855 for (int i = 0; i < cl->class_class_member_count; ++i)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001856 {
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001857 ocmember_T *m = &cl->class_class_members[i];
1858 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001859 {
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001860 if (cl_ret != NULL)
1861 *cl_ret = cl;
1862 return i;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001863 }
1864 }
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001865 return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001866}
1867
1868/*
Bram Moolenaar62a69232023-01-24 15:07:04 +00001869 * Return TRUE if current context "cctx_arg" is inside class "cl".
1870 * Return FALSE if not.
1871 */
1872 int
1873inside_class(cctx_T *cctx_arg, class_T *cl)
1874{
1875 for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
1876 if (cctx->ctx_ufunc != NULL && cctx->ctx_ufunc->uf_class == cl)
1877 return TRUE;
1878 return FALSE;
1879}
1880
1881/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001882 * Make a copy of an object.
1883 */
1884 void
1885copy_object(typval_T *from, typval_T *to)
1886{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02001887 if (from->vval.v_object == NULL)
1888 to->vval.v_object = NULL;
1889 else
1890 {
1891 to->vval.v_object = from->vval.v_object;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001892 ++to->vval.v_object->obj_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02001893 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001894}
1895
1896/*
1897 * Free an object.
1898 */
1899 static void
1900object_clear(object_T *obj)
1901{
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01001902 // Avoid a recursive call, it can happen if "obj" has a circular reference.
1903 obj->obj_refcount = INT_MAX;
1904
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001905 class_T *cl = obj->obj_class;
1906
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02001907 if (!cl)
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02001908 return;
Jia-Ju Bai5b0889b2023-08-13 20:04:04 +02001909
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001910 // the member values are just after the object structure
1911 typval_T *tv = (typval_T *)(obj + 1);
1912 for (int i = 0; i < cl->class_obj_member_count; ++i)
1913 clear_tv(tv + i);
1914
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001915 // Remove from the list headed by "first_object".
1916 object_cleared(obj);
1917
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001918 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001919 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001920}
1921
1922/*
1923 * Unreference an object.
1924 */
1925 void
1926object_unref(object_T *obj)
1927{
1928 if (obj != NULL && --obj->obj_refcount <= 0)
1929 object_clear(obj);
1930}
1931
1932/*
1933 * Make a copy of a class.
1934 */
1935 void
1936copy_class(typval_T *from, typval_T *to)
1937{
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02001938 if (from->vval.v_class == NULL)
1939 to->vval.v_class = NULL;
1940 else
1941 {
1942 to->vval.v_class = from->vval.v_class;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001943 ++to->vval.v_class->class_refcount;
Yegappan Lakshmanan618e47d2023-08-22 21:29:28 +02001944 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001945}
1946
1947/*
1948 * Unreference a class. Free it when the reference count goes down to zero.
1949 */
1950 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001951class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001952{
Bram Moolenaard505d172022-12-18 21:42:55 +00001953 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001954 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001955 // Freeing what the class contains may recursively come back here.
1956 // Clear "class_name" first, if it is NULL the class does not need to
1957 // be freed.
1958 VIM_CLEAR(cl->class_name);
1959
Bram Moolenaar83677162023-01-08 19:54:10 +00001960 class_unref(cl->class_extends);
1961
Bram Moolenaar94674f22023-01-06 18:42:20 +00001962 for (int i = 0; i < cl->class_interface_count; ++i)
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001963 {
Bram Moolenaar94674f22023-01-06 18:42:20 +00001964 vim_free(((char_u **)cl->class_interfaces)[i]);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001965 if (cl->class_interfaces_cl[i] != NULL)
1966 class_unref(cl->class_interfaces_cl[i]);
1967 }
Bram Moolenaar94674f22023-01-06 18:42:20 +00001968 vim_free(cl->class_interfaces);
Bram Moolenaara94bd9d2023-01-12 15:01:32 +00001969 vim_free(cl->class_interfaces_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +00001970
Bram Moolenaar29ac5df2023-01-16 19:43:47 +00001971 itf2class_T *next;
1972 for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
1973 {
1974 next = i2c->i2c_next;
1975 vim_free(i2c);
1976 }
1977
Bram Moolenaard505d172022-12-18 21:42:55 +00001978 for (int i = 0; i < cl->class_class_member_count; ++i)
1979 {
1980 ocmember_T *m = &cl->class_class_members[i];
1981 vim_free(m->ocm_name);
1982 vim_free(m->ocm_init);
1983 if (cl->class_members_tv != NULL)
1984 clear_tv(&cl->class_members_tv[i]);
1985 }
1986 vim_free(cl->class_class_members);
1987 vim_free(cl->class_members_tv);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001988
1989 for (int i = 0; i < cl->class_obj_member_count; ++i)
1990 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001991 ocmember_T *m = &cl->class_obj_members[i];
1992 vim_free(m->ocm_name);
1993 vim_free(m->ocm_init);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001994 }
1995 vim_free(cl->class_obj_members);
1996
Bram Moolenaarec8b74f2023-01-01 14:11:27 +00001997 for (int i = 0; i < cl->class_class_function_count; ++i)
1998 {
1999 ufunc_T *uf = cl->class_class_functions[i];
2000 func_clear_free(uf, FALSE);
2001 }
2002 vim_free(cl->class_class_functions);
2003
Bram Moolenaarffdaca92022-12-09 21:41:48 +00002004 for (int i = 0; i < cl->class_obj_method_count; ++i)
2005 {
2006 ufunc_T *uf = cl->class_obj_methods[i];
2007 func_clear_free(uf, FALSE);
2008 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002009 vim_free(cl->class_obj_methods);
2010
Bram Moolenaar00b28d62022-12-08 15:32:33 +00002011 clear_type_list(&cl->class_type_list);
2012
2013 vim_free(cl);
2014 }
2015}
2016
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002017static object_T *first_object = NULL;
2018
2019/*
2020 * Call this function when an object has been created. It will be added to the
2021 * list headed by "first_object".
2022 */
2023 void
2024object_created(object_T *obj)
2025{
2026 if (first_object != NULL)
2027 {
2028 obj->obj_next_used = first_object;
2029 first_object->obj_prev_used = obj;
2030 }
2031 first_object = obj;
2032}
2033
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002034static object_T *next_nonref_obj = NULL;
2035
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002036/*
2037 * Call this function when an object has been cleared and is about to be freed.
2038 * It is removed from the list headed by "first_object".
2039 */
2040 void
2041object_cleared(object_T *obj)
2042{
2043 if (obj->obj_next_used != NULL)
2044 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
2045 if (obj->obj_prev_used != NULL)
2046 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
2047 else if (first_object == obj)
2048 first_object = obj->obj_next_used;
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002049
2050 // update the next object to check if needed
2051 if (obj == next_nonref_obj)
2052 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002053}
2054
2055/*
2056 * Go through the list of all objects and free items without "copyID".
2057 */
2058 int
2059object_free_nonref(int copyID)
2060{
2061 int did_free = FALSE;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002062
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002063 for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002064 {
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002065 next_nonref_obj = obj->obj_next_used;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002066 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
2067 {
2068 // Free the object and items it contains.
2069 object_clear(obj);
2070 did_free = TRUE;
2071 }
2072 }
2073
Bram Moolenaarf7ca56f2023-06-05 16:53:25 +01002074 next_nonref_obj = NULL;
Bram Moolenaard28d7b92022-12-08 20:42:00 +00002075 return did_free;
2076}
2077
LemonBoyafe04662023-08-23 21:08:11 +02002078/*
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002079 * Return TRUE when the class "cl", its base class or one of the implemented
2080 * interfaces matches the class "other_cl".
LemonBoyafe04662023-08-23 21:08:11 +02002081 */
2082 int
2083class_instance_of(class_T *cl, class_T *other_cl)
2084{
2085 if (cl == other_cl)
2086 return TRUE;
2087
2088 // Recursively check the base classes.
2089 for (; cl != NULL; cl = cl->class_extends)
2090 {
2091 if (cl == other_cl)
2092 return TRUE;
2093 // Check the implemented interfaces.
2094 for (int i = cl->class_interface_count - 1; i >= 0; --i)
2095 if (cl->class_interfaces_cl[i] == other_cl)
2096 return TRUE;
2097 }
2098
2099 return FALSE;
2100}
2101
2102/*
2103 * "instanceof(object, classinfo)" function
2104 */
2105 void
2106f_instanceof(typval_T *argvars, typval_T *rettv)
2107{
2108 typval_T *object_tv = &argvars[0];
2109 typval_T *classinfo_tv = &argvars[1];
2110 listitem_T *li;
2111
2112 rettv->vval.v_number = VVAL_FALSE;
2113
2114 if (check_for_object_arg(argvars, 0) == FAIL
2115 || check_for_class_or_list_arg(argvars, 1) == FAIL)
2116 return;
2117
2118 if (classinfo_tv->v_type == VAR_LIST)
2119 {
2120 FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
2121 {
2122 if (li->li_tv.v_type != VAR_CLASS)
2123 {
2124 emsg(_(e_class_required));
2125 return;
2126 }
2127
2128 if (class_instance_of(object_tv->vval.v_object->obj_class,
2129 li->li_tv.vval.v_class) == TRUE)
2130 {
2131 rettv->vval.v_number = VVAL_TRUE;
2132 return;
2133 }
2134 }
2135 }
2136 else if (classinfo_tv->v_type == VAR_CLASS)
2137 {
2138 rettv->vval.v_number = class_instance_of(object_tv->vval.v_object->obj_class,
2139 classinfo_tv->vval.v_class);
2140 }
2141}
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002142
2143#endif // FEAT_EVAL