blob: 9b1f91334587d462b9482db7b132a3bebf48b151 [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);
151 m->ocm_access = has_public ? ACCESS_ALL
152 : *varname == '_' ? ACCESS_PRIVATE : ACCESS_READ;
153 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
185 *members[i] = parent_members[i];
186 members[i]->ocm_name = vim_strsave(members[i]->ocm_name);
187 if (members[i]->ocm_init != NULL)
188 members[i]->ocm_init = vim_strsave(members[i]->ocm_init);
189 }
Bram Moolenaar8efdcee2022-12-19 12:18:09 +0000190 if (gap->ga_len > 0)
Bram Moolenaar83677162023-01-08 19:54:10 +0000191 // new members are moved
192 mch_memmove(*members + parent_count,
193 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
Bram Moolenaard505d172022-12-18 21:42:55 +0000194 VIM_CLEAR(gap->ga_data);
195 return OK;
196}
197
198/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000199 * Handle ":class" and ":abstract class" up to ":endclass".
Bram Moolenaar554d0312023-01-05 19:59:18 +0000200 * Handle ":interface" up to ":endinterface".
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000201 */
202 void
203ex_class(exarg_T *eap)
204{
Bram Moolenaar554d0312023-01-05 19:59:18 +0000205 int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
206
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000207 if (!current_script_is_vim9()
208 || (cmdmod.cmod_flags & CMOD_LEGACY)
209 || !getline_equal(eap->getline, eap->cookie, getsourceline))
210 {
Bram Moolenaar554d0312023-01-05 19:59:18 +0000211 if (is_class)
212 emsg(_(e_class_can_only_be_defined_in_vim9_script));
213 else
214 emsg(_(e_interface_can_only_be_defined_in_vim9_script));
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000215 return;
216 }
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000217
218 char_u *arg = eap->arg;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000219 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000220 if (is_abstract)
221 {
222 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
223 {
224 semsg(_(e_invalid_argument_str), arg);
225 return;
226 }
227 arg = skipwhite(arg + 5);
228 }
229
230 if (!ASCII_ISUPPER(*arg))
231 {
Bram Moolenaar554d0312023-01-05 19:59:18 +0000232 if (is_class)
233 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
234 else
235 semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
236 arg);
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000237 return;
238 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000239 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
240 if (!IS_WHITE_OR_NUL(*name_end))
241 {
Bram Moolenaar554d0312023-01-05 19:59:18 +0000242 semsg(_(e_white_space_required_after_name_str), arg);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000243 return;
244 }
Bram Moolenaar94674f22023-01-06 18:42:20 +0000245 char_u *name_start = arg;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000246
247 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000248 // generics: <Tkey, Tentry>
Bram Moolenaard505d172022-12-18 21:42:55 +0000249 // handle "is_export" if it is set
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000250
Bram Moolenaar83677162023-01-08 19:54:10 +0000251 // Name for "extends BaseClass"
252 char_u *extends = NULL;
253
Bram Moolenaar94674f22023-01-06 18:42:20 +0000254 // Names for "implements SomeInterface"
255 garray_T ga_impl;
256 ga_init2(&ga_impl, sizeof(char_u *), 5);
257
258 arg = skipwhite(name_end);
259 while (*arg != NUL && *arg != '#' && *arg != '\n')
260 {
261 // TODO:
Bram Moolenaar94674f22023-01-06 18:42:20 +0000262 // specifies SomeInterface
Bram Moolenaar83677162023-01-08 19:54:10 +0000263 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
264 {
265 if (extends != NULL)
266 {
267 emsg(_(e_duplicate_extends));
268 goto early_ret;
269 }
270 arg = skipwhite(arg + 7);
271 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
272 if (!IS_WHITE_OR_NUL(*end))
273 {
274 semsg(_(e_white_space_required_after_name_str), arg);
275 goto early_ret;
276 }
277 extends = vim_strnsave(arg, end - arg);
278 if (extends == NULL)
279 goto early_ret;
280
281 arg = skipwhite(end + 1);
282 }
283 else if (STRNCMP(arg, "implements", 10) == 0
284 && IS_WHITE_OR_NUL(arg[10]))
Bram Moolenaar94674f22023-01-06 18:42:20 +0000285 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +0000286 if (ga_impl.ga_len > 0)
287 {
288 emsg(_(e_duplicate_implements));
289 goto early_ret;
290 }
Bram Moolenaar94674f22023-01-06 18:42:20 +0000291 arg = skipwhite(arg + 10);
Bram Moolenaardf8f9472023-01-07 14:51:03 +0000292
293 for (;;)
Bram Moolenaar94674f22023-01-06 18:42:20 +0000294 {
Bram Moolenaardf8f9472023-01-07 14:51:03 +0000295 char_u *impl_end = find_name_end(arg, NULL, NULL,
296 FNE_CHECK_START);
297 if (!IS_WHITE_OR_NUL(*impl_end) && *impl_end != ',')
298 {
299 semsg(_(e_white_space_required_after_name_str), arg);
300 goto early_ret;
301 }
302 char_u *iname = vim_strnsave(arg, impl_end - arg);
303 if (iname == NULL)
304 goto early_ret;
305 for (int i = 0; i < ga_impl.ga_len; ++i)
306 if (STRCMP(((char_u **)ga_impl.ga_data)[i], iname) == 0)
307 {
308 semsg(_(e_duplicate_interface_after_implements_str),
309 iname);
310 vim_free(iname);
311 goto early_ret;
312 }
313 if (ga_add_string(&ga_impl, iname) == FAIL)
314 {
315 vim_free(iname);
316 goto early_ret;
317 }
318 if (*impl_end != ',')
319 {
320 arg = skipwhite(impl_end);
321 break;
322 }
323 arg = skipwhite(impl_end + 1);
Bram Moolenaar94674f22023-01-06 18:42:20 +0000324 }
Bram Moolenaar94674f22023-01-06 18:42:20 +0000325 }
326 else
327 {
328 semsg(_(e_trailing_characters_str), arg);
329early_ret:
Bram Moolenaar83677162023-01-08 19:54:10 +0000330 vim_free(extends);
Bram Moolenaar94674f22023-01-06 18:42:20 +0000331 ga_clear_strings(&ga_impl);
332 return;
333 }
334 }
335
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000336 garray_T type_list; // list of pointers to allocated types
337 ga_init2(&type_list, sizeof(type_T *), 10);
338
Bram Moolenaard505d172022-12-18 21:42:55 +0000339 // Growarray with class members declared in the class.
340 garray_T classmembers;
341 ga_init2(&classmembers, sizeof(ocmember_T), 10);
342
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000343 // Growarray with functions declared in the class.
344 garray_T classfunctions;
345 ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
Bram Moolenaard505d172022-12-18 21:42:55 +0000346
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000347 // Growarray with object members declared in the class.
348 garray_T objmembers;
Bram Moolenaard505d172022-12-18 21:42:55 +0000349 ga_init2(&objmembers, sizeof(ocmember_T), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000350
351 // Growarray with object methods declared in the class.
352 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000353 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000354
355 /*
Bram Moolenaar554d0312023-01-05 19:59:18 +0000356 * Go over the body of the class/interface until "endclass" or
357 * "endinterface" is found.
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000358 */
359 char_u *theline = NULL;
360 int success = FALSE;
361 for (;;)
362 {
363 vim_free(theline);
364 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
365 if (theline == NULL)
366 break;
367 char_u *line = skipwhite(theline);
368
Bram Moolenaar418b5472022-12-20 13:38:22 +0000369 // Skip empty and comment lines.
370 if (*line == NUL)
371 continue;
372 if (*line == '#')
373 {
374 if (vim9_bad_comment(line))
375 break;
376 continue;
377 }
378
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000379 char_u *p = line;
Bram Moolenaar554d0312023-01-05 19:59:18 +0000380 char *end_name = is_class ? "endclass" : "endinterface";
381 if (checkforcmd(&p, end_name, is_class ? 4 : 5))
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000382 {
Bram Moolenaar554d0312023-01-05 19:59:18 +0000383 if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000384 semsg(_(e_command_cannot_be_shortened_str), line);
385 else if (*p == '|' || !ends_excmd2(line, p))
386 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +0000387 else
388 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000389 break;
390 }
Bram Moolenaar554d0312023-01-05 19:59:18 +0000391 char *wrong_name = is_class ? "endinterface" : "endclass";
392 if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
393 {
394 semsg(_(e_invalid_command_str), line);
395 break;
396 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000397
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000398 int has_public = FALSE;
399 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000400 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000401 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000402 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000403 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000404 break;
405 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000406 has_public = TRUE;
407 p = skipwhite(line + 6);
408
Bram Moolenaard505d172022-12-18 21:42:55 +0000409 if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000410 {
Bram Moolenaard505d172022-12-18 21:42:55 +0000411 emsg(_(e_public_must_be_followed_by_this_or_static));
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000412 break;
413 }
414 }
Bram Moolenaard505d172022-12-18 21:42:55 +0000415
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000416 int has_static = FALSE;
417 char_u *ps = p;
418 if (checkforcmd(&p, "static", 4))
419 {
420 if (STRNCMP(ps, "static", 6) != 0)
421 {
422 semsg(_(e_command_cannot_be_shortened_str), ps);
423 break;
424 }
425 has_static = TRUE;
426 p = skipwhite(ps + 6);
427 }
428
Bram Moolenaard505d172022-12-18 21:42:55 +0000429 // object members (public, read access, private):
430 // "this._varname"
431 // "this.varname"
432 // "public this.varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000433 if (STRNCMP(p, "this", 4) == 0)
434 {
435 if (p[4] != '.' || !eval_isnamec1(p[5]))
436 {
437 semsg(_(e_invalid_object_member_declaration_str), p);
438 break;
439 }
440 char_u *varname = p + 5;
Bram Moolenaard505d172022-12-18 21:42:55 +0000441 char_u *varname_end = NULL;
Bram Moolenaar74e12742022-12-13 21:14:28 +0000442 type_T *type = NULL;
Bram Moolenaard505d172022-12-18 21:42:55 +0000443 char_u *init_expr = NULL;
444 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +0000445 &varname_end, &type_list, &type,
446 is_class ? &init_expr: NULL) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000447 break;
448 if (add_member(&objmembers, varname, varname_end,
449 has_public, type, init_expr) == FAIL)
Bram Moolenaar74e12742022-12-13 21:14:28 +0000450 {
Bram Moolenaard505d172022-12-18 21:42:55 +0000451 vim_free(init_expr);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000452 break;
453 }
Bram Moolenaard505d172022-12-18 21:42:55 +0000454 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000455
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000456 // constructors:
457 // def new()
458 // enddef
459 // def newOther()
460 // enddef
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000461 // object methods and class functions:
462 // def SomeMethod()
463 // enddef
464 // static def ClassFunction()
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000465 // enddef
466 // TODO:
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000467 // def <Tval> someMethod()
468 // enddef
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000469 else if (checkforcmd(&p, "def", 3))
470 {
471 exarg_T ea;
472 garray_T lines_to_free;
473
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000474 // TODO: error for "public static def Func()"?
475
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000476 CLEAR_FIELD(ea);
477 ea.cmd = line;
478 ea.arg = p;
479 ea.cmdidx = CMD_def;
480 ea.getline = eap->getline;
481 ea.cookie = eap->cookie;
482
483 ga_init2(&lines_to_free, sizeof(char_u *), 50);
Bram Moolenaar554d0312023-01-05 19:59:18 +0000484 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
485 is_class ? CF_CLASS : CF_INTERFACE);
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000486 ga_clear_strings(&lines_to_free);
487
Bram Moolenaar6acf7572023-01-01 19:53:30 +0000488 if (uf != NULL)
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000489 {
Bram Moolenaar6acf7572023-01-01 19:53:30 +0000490 int is_new = STRNCMP(uf->uf_name, "new", 3) == 0;
491 garray_T *fgap = has_static || is_new
492 ? &classfunctions : &objmethods;
493 if (ga_grow(fgap, 1) == OK)
494 {
495 if (is_new)
496 uf->uf_flags |= FC_NEW;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000497
Bram Moolenaar6acf7572023-01-01 19:53:30 +0000498 ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
499 ++fgap->ga_len;
500 }
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000501 }
502 }
503
504 // class members
505 else if (has_static)
506 {
507 // class members (public, read access, private):
508 // "static _varname"
509 // "static varname"
510 // "public static varname"
511 char_u *varname = p;
512 char_u *varname_end = NULL;
513 type_T *type = NULL;
514 char_u *init_expr = NULL;
515 if (parse_member(eap, line, varname, has_public,
Bram Moolenaar554d0312023-01-05 19:59:18 +0000516 &varname_end, &type_list, &type,
517 is_class ? &init_expr : NULL) == FAIL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000518 break;
519 if (add_member(&classmembers, varname, varname_end,
520 has_public, type, init_expr) == FAIL)
521 {
522 vim_free(init_expr);
523 break;
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000524 }
525 }
526
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000527 else
528 {
Bram Moolenaar554d0312023-01-05 19:59:18 +0000529 if (is_class)
530 semsg(_(e_not_valid_command_in_class_str), line);
531 else
532 semsg(_(e_not_valid_command_in_interface_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000533 break;
534 }
535 }
536 vim_free(theline);
537
Bram Moolenaar83677162023-01-08 19:54:10 +0000538 class_T *extends_cl = NULL; // class from "extends" argument
539
540 /*
541 * Check a few things before defining the class.
542 */
543
544 // Check the "extends" class is valid.
545 if (success && extends != NULL)
546 {
547 typval_T tv;
548 tv.v_type = VAR_UNKNOWN;
549 if (eval_variable(extends, 0, 0, &tv, NULL, EVAL_VAR_IMPORT) == FAIL)
550 {
551 semsg(_(e_class_name_not_found_str), extends);
552 success = FALSE;
553 }
554 else
555 {
556 if (tv.v_type != VAR_CLASS
557 || tv.vval.v_class == NULL
558 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
559 {
560 semsg(_(e_cannot_extend_str), extends);
561 success = FALSE;
562 }
563 else
564 {
565 extends_cl = tv.vval.v_class;
566 ++extends_cl->class_refcount;
567 }
568 clear_tv(&tv);
569 }
570 }
571 VIM_CLEAR(extends);
572
573 // Check all "implements" entries are valid.
Bram Moolenaar94674f22023-01-06 18:42:20 +0000574 if (success && ga_impl.ga_len > 0)
575 {
Bram Moolenaar94674f22023-01-06 18:42:20 +0000576 for (int i = 0; i < ga_impl.ga_len && success; ++i)
577 {
578 char_u *impl = ((char_u **)ga_impl.ga_data)[i];
579 typval_T tv;
580 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar83677162023-01-08 19:54:10 +0000581 if (eval_variable(impl, 0, 0, &tv, NULL, EVAL_VAR_IMPORT) == FAIL)
Bram Moolenaar94674f22023-01-06 18:42:20 +0000582 {
583 semsg(_(e_interface_name_not_found_str), impl);
584 success = FALSE;
585 break;
586 }
587
588 if (tv.v_type != VAR_CLASS
589 || tv.vval.v_class == NULL
590 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
591 {
592 semsg(_(e_not_valid_interface_str), impl);
593 success = FALSE;
594 }
595
596 // check the members of the interface match the members of the class
597 class_T *ifcl = tv.vval.v_class;
598 for (int loop = 1; loop <= 2 && success; ++loop)
599 {
600 // loop == 1: check class members
601 // loop == 2: check object members
602 int if_count = loop == 1 ? ifcl->class_class_member_count
603 : ifcl->class_obj_member_count;
604 if (if_count == 0)
605 continue;
606 ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
607 : ifcl->class_obj_members;
608 ocmember_T *cl_ms = (ocmember_T *)(loop == 1
609 ? classmembers.ga_data
610 : objmembers.ga_data);
611 int cl_count = loop == 1 ? classmembers.ga_len
612 : objmembers.ga_len;
613 for (int if_i = 0; if_i < if_count; ++if_i)
614 {
615 int cl_i;
616 for (cl_i = 0; cl_i < cl_count; ++cl_i)
617 {
618 ocmember_T *m = &cl_ms[cl_i];
619 if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) == 0)
620 {
621 // TODO: check type
622 break;
623 }
624 }
625 if (cl_i == cl_count)
626 {
627 semsg(_(e_member_str_of_interface_str_not_implemented),
628 if_ms[if_i].ocm_name, impl);
629 success = FALSE;
630 break;
631 }
632 }
633 }
634
635 // check the functions/methods of the interface match the
636 // functions/methods of the class
637 for (int loop = 1; loop <= 2 && success; ++loop)
638 {
639 // loop == 1: check class functions
640 // loop == 2: check object methods
641 int if_count = loop == 1 ? ifcl->class_class_function_count
642 : ifcl->class_obj_method_count;
643 if (if_count == 0)
644 continue;
645 ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
646 : ifcl->class_obj_methods;
647 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
648 ? classfunctions.ga_data
649 : objmethods.ga_data);
650 int cl_count = loop == 1 ? classfunctions.ga_len
651 : objmethods.ga_len;
652 for (int if_i = 0; if_i < if_count; ++if_i)
653 {
654 char_u *if_name = if_fp[if_i]->uf_name;
655 int cl_i;
656 for (cl_i = 0; cl_i < cl_count; ++cl_i)
657 {
658 char_u *cl_name = cl_fp[cl_i]->uf_name;
659 if (STRCMP(if_name, cl_name) == 0)
660 {
661 // TODO: check return and argument types
662 break;
663 }
664 }
665 if (cl_i == cl_count)
666 {
667 semsg(_(e_function_str_of_interface_str_not_implemented),
668 if_name, impl);
669 success = FALSE;
670 break;
671 }
672 }
673 }
674
675 clear_tv(&tv);
676 }
677 }
678
Bram Moolenaareb533502022-12-14 15:06:11 +0000679 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000680 if (success)
681 {
Bram Moolenaard505d172022-12-18 21:42:55 +0000682 // "endclass" encountered without failures: Create the class.
683
Bram Moolenaareb533502022-12-14 15:06:11 +0000684 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000685 if (cl == NULL)
686 goto cleanup;
Bram Moolenaar554d0312023-01-05 19:59:18 +0000687 if (!is_class)
688 cl->class_flags = CLASS_INTERFACE;
689
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000690 cl->class_refcount = 1;
Bram Moolenaar94674f22023-01-06 18:42:20 +0000691 cl->class_name = vim_strnsave(name_start, name_end - name_start);
Bram Moolenaard505d172022-12-18 21:42:55 +0000692 if (cl->class_name == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000693 goto cleanup;
Bram Moolenaard505d172022-12-18 21:42:55 +0000694
Bram Moolenaar83677162023-01-08 19:54:10 +0000695 cl->class_extends = extends_cl;
696
Bram Moolenaar94674f22023-01-06 18:42:20 +0000697 if (ga_impl.ga_len > 0)
698 {
699 // Move the "implements" names into the class.
700 cl->class_interface_count = ga_impl.ga_len;
701 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
702 if (cl->class_interfaces == NULL)
703 goto cleanup;
704 for (int i = 0; i < ga_impl.ga_len; ++i)
705 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
Bram Moolenaar7d4d87b2023-01-06 18:59:08 +0000706 VIM_CLEAR(ga_impl.ga_data);
Bram Moolenaar94674f22023-01-06 18:42:20 +0000707 ga_impl.ga_len = 0;
708 }
709
Bram Moolenaard505d172022-12-18 21:42:55 +0000710 // Add class and object members to "cl".
711 if (add_members_to_class(&classmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +0000712 extends_cl == NULL ? NULL
713 : extends_cl->class_class_members,
714 extends_cl == NULL ? 0
715 : extends_cl->class_class_member_count,
716 &cl->class_class_members,
717 &cl->class_class_member_count) == FAIL
Bram Moolenaard505d172022-12-18 21:42:55 +0000718 || add_members_to_class(&objmembers,
Bram Moolenaar83677162023-01-08 19:54:10 +0000719 extends_cl == NULL ? NULL
720 : extends_cl->class_obj_members,
721 extends_cl == NULL ? 0
722 : extends_cl->class_obj_member_count,
723 &cl->class_obj_members,
724 &cl->class_obj_member_count) == FAIL)
Bram Moolenaard505d172022-12-18 21:42:55 +0000725 goto cleanup;
726
Bram Moolenaar554d0312023-01-05 19:59:18 +0000727 if (is_class && cl->class_class_member_count > 0)
Bram Moolenaard505d172022-12-18 21:42:55 +0000728 {
729 // Allocate a typval for each class member and initialize it.
730 cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
731 cl->class_class_member_count);
732 if (cl->class_members_tv != NULL)
733 for (int i = 0; i < cl->class_class_member_count; ++i)
734 {
735 ocmember_T *m = &cl->class_class_members[i];
736 typval_T *tv = &cl->class_members_tv[i];
737 if (m->ocm_init != NULL)
738 {
739 typval_T *etv = eval_expr(m->ocm_init, eap);
740 if (etv != NULL)
741 {
742 *tv = *etv;
743 vim_free(etv);
744 }
745 }
746 else
747 {
748 // TODO: proper default value
749 tv->v_type = m->ocm_type->tt_type;
750 tv->vval.v_string = NULL;
751 }
752 }
753 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000754
755 int have_new = FALSE;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000756 for (int i = 0; i < classfunctions.ga_len; ++i)
757 if (STRCMP(((ufunc_T **)classfunctions.ga_data)[i]->uf_name,
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000758 "new") == 0)
759 {
760 have_new = TRUE;
761 break;
762 }
Bram Moolenaar94674f22023-01-06 18:42:20 +0000763 if (is_class && !have_new)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000764 {
765 // No new() method was defined, add the default constructor.
766 garray_T fga;
767 ga_init2(&fga, 1, 1000);
768 ga_concat(&fga, (char_u *)"new(");
769 for (int i = 0; i < cl->class_obj_member_count; ++i)
770 {
771 if (i > 0)
772 ga_concat(&fga, (char_u *)", ");
773 ga_concat(&fga, (char_u *)"this.");
Bram Moolenaard505d172022-12-18 21:42:55 +0000774 ocmember_T *m = cl->class_obj_members + i;
775 ga_concat(&fga, (char_u *)m->ocm_name);
Bram Moolenaar65b0d162022-12-13 18:43:22 +0000776 ga_concat(&fga, (char_u *)" = v:none");
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000777 }
778 ga_concat(&fga, (char_u *)")\nenddef\n");
779 ga_append(&fga, NUL);
780
781 exarg_T fea;
782 CLEAR_FIELD(fea);
783 fea.cmdidx = CMD_def;
784 fea.cmd = fea.arg = fga.ga_data;
785
786 garray_T lines_to_free;
787 ga_init2(&lines_to_free, sizeof(char_u *), 50);
788
Bram Moolenaar2c011312023-01-07 10:51:30 +0000789 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000790
791 ga_clear_strings(&lines_to_free);
792 vim_free(fga.ga_data);
793
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000794 if (nf != NULL && ga_grow(&classfunctions, 1) == OK)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000795 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000796 ((ufunc_T **)classfunctions.ga_data)[classfunctions.ga_len] = nf;
797 ++classfunctions.ga_len;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000798
799 nf->uf_flags |= FC_NEW;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000800 nf->uf_ret_type = get_type_ptr(&type_list);
801 if (nf->uf_ret_type != NULL)
802 {
803 nf->uf_ret_type->tt_type = VAR_OBJECT;
804 nf->uf_ret_type->tt_member = (type_T *)cl;
805 nf->uf_ret_type->tt_argcount = 0;
806 nf->uf_ret_type->tt_args = NULL;
807 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000808 }
809 }
810
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000811 // loop 1: class functions, loop 2: object methods
812 for (int loop = 1; loop <= 2; ++loop)
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000813 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000814 garray_T *gap = loop == 1 ? &classfunctions : &objmethods;
815 int *fcount = loop == 1 ? &cl->class_class_function_count
816 : &cl->class_obj_method_count;
817 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
818 : &cl->class_obj_methods;
819
Bram Moolenaar83677162023-01-08 19:54:10 +0000820 int parent_count = 0;
821 if (extends_cl != NULL)
822 // Include functions from the parent.
823 parent_count = loop == 1
824 ? extends_cl->class_class_function_count
825 : extends_cl->class_obj_method_count;
826
827 *fcount = parent_count + gap->ga_len;
828 if (*fcount == 0)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000829 {
830 *fup = NULL;
831 continue;
832 }
Bram Moolenaar83677162023-01-08 19:54:10 +0000833 *fup = ALLOC_MULT(ufunc_T *, *fcount);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000834 if (*fup == NULL)
835 goto cleanup;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000836
Bram Moolenaar83677162023-01-08 19:54:10 +0000837 int skipped = 0;
838 for (int i = 0; i < parent_count; ++i)
839 {
840 // Copy functions from the parent. Can't use the same
841 // function, because "uf_class" is different and compilation
842 // will have a different result.
843 // Skip "new" functions. TODO: not all of them.
844 if (loop == 1 && STRNCMP(
845 extends_cl->class_class_functions[i]->uf_name,
846 "new", 3) == 0)
847 ++skipped;
848 else
849 *fup[i - skipped] = copy_function((loop == 1
850 ? extends_cl->class_class_functions
851 : extends_cl->class_obj_methods)[i]);
852 }
853
854 mch_memmove(*fup + parent_count - skipped, gap->ga_data,
855 sizeof(ufunc_T *) * gap->ga_len);
856 vim_free(gap->ga_data);
857 *fcount -= skipped;
858
859 // Set the class pointer on all the functions and object methods.
860 for (int i = 0; i < *fcount; ++i)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000861 {
862 ufunc_T *fp = (*fup)[i];
863 fp->uf_class = cl;
864 if (loop == 2)
865 fp->uf_flags |= FC_OBJECT;
866 }
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000867 }
868
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000869 cl->class_type.tt_type = VAR_CLASS;
870 cl->class_type.tt_member = (type_T *)cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000871 cl->class_object_type.tt_type = VAR_OBJECT;
872 cl->class_object_type.tt_member = (type_T *)cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000873 cl->class_type_list = type_list;
874
875 // TODO:
Bram Moolenaard505d172022-12-18 21:42:55 +0000876 // - Fill hashtab with object members and methods ?
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000877
878 // Add the class to the script-local variables.
Bram Moolenaar94674f22023-01-06 18:42:20 +0000879 // TODO: handle other context, e.g. in a function
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000880 typval_T tv;
881 tv.v_type = VAR_CLASS;
882 tv.vval.v_class = cl;
883 set_var_const(cl->class_name, current_sctx.sc_sid,
884 NULL, &tv, FALSE, ASSIGN_DECL, 0);
885 return;
886 }
887
888cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +0000889 if (cl != NULL)
890 {
891 vim_free(cl->class_name);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000892 vim_free(cl->class_class_functions);
Bram Moolenaareb533502022-12-14 15:06:11 +0000893 vim_free(cl->class_obj_members);
894 vim_free(cl->class_obj_methods);
895 vim_free(cl);
896 }
897
Bram Moolenaar83677162023-01-08 19:54:10 +0000898 vim_free(extends);
899 class_unref(extends_cl);
Bram Moolenaar94674f22023-01-06 18:42:20 +0000900 ga_clear_strings(&ga_impl);
901
Bram Moolenaard505d172022-12-18 21:42:55 +0000902 for (int round = 1; round <= 2; ++round)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000903 {
Bram Moolenaard505d172022-12-18 21:42:55 +0000904 garray_T *gap = round == 1 ? &classmembers : &objmembers;
905 if (gap->ga_len == 0 || gap->ga_data == NULL)
906 continue;
907
908 for (int i = 0; i < gap->ga_len; ++i)
909 {
910 ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
911 vim_free(m->ocm_name);
912 vim_free(m->ocm_init);
913 }
914 ga_clear(gap);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000915 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000916
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000917 for (int i = 0; i < objmethods.ga_len; ++i)
918 {
919 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
920 func_clear_free(uf, FALSE);
921 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000922 ga_clear(&objmethods);
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000923
924 for (int i = 0; i < classfunctions.ga_len; ++i)
925 {
926 ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
927 func_clear_free(uf, FALSE);
928 }
929 ga_clear(&classfunctions);
930
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000931 clear_type_list(&type_list);
932}
933
934/*
Bram Moolenaarf54cedd2022-12-23 17:56:27 +0000935 * Find member "name" in class "cl", set "member_idx" to the member index and
936 * return its type.
937 * When not found "member_idx" is set to -1 and t_any is returned.
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000938 */
939 type_T *
940class_member_type(
941 class_T *cl,
942 char_u *name,
943 char_u *name_end,
944 int *member_idx)
945{
946 *member_idx = -1; // not found (yet)
947 size_t len = name_end - name;
948
949 for (int i = 0; i < cl->class_obj_member_count; ++i)
950 {
Bram Moolenaard505d172022-12-18 21:42:55 +0000951 ocmember_T *m = cl->class_obj_members + i;
952 if (STRNCMP(m->ocm_name, name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000953 {
954 *member_idx = i;
Bram Moolenaard505d172022-12-18 21:42:55 +0000955 return m->ocm_type;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000956 }
957 }
Bram Moolenaarf54cedd2022-12-23 17:56:27 +0000958
959 semsg(_(e_unknown_variable_str), name);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000960 return &t_any;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000961}
962
963/*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000964 * Handle ":enum" up to ":endenum".
965 */
966 void
967ex_enum(exarg_T *eap UNUSED)
968{
969 // TODO
970}
971
972/*
973 * Handle ":type".
974 */
975 void
976ex_type(exarg_T *eap UNUSED)
977{
978 // TODO
979}
980
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000981/*
982 * Evaluate what comes after a class:
983 * - class member: SomeClass.varname
Bram Moolenaar6bafdd42023-01-01 12:58:33 +0000984 * - class function: SomeClass.SomeMethod()
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000985 * - class constructor: SomeClass.new()
986 * - object member: someObject.varname
987 * - object method: someObject.SomeMethod()
988 *
989 * "*arg" points to the '.'.
990 * "*arg" is advanced to after the member name or method call.
991 *
992 * Returns FAIL or OK.
993 */
994 int
995class_object_index(
996 char_u **arg,
997 typval_T *rettv,
998 evalarg_T *evalarg,
999 int verbose UNUSED) // give error messages
1000{
1001 // int evaluate = evalarg != NULL
1002 // && (evalarg->eval_flags & EVAL_EVALUATE);
1003
1004 if (VIM_ISWHITE((*arg)[1]))
1005 {
1006 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
1007 return FAIL;
1008 }
1009
1010 ++*arg;
1011 char_u *name = *arg;
1012 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
1013 if (name_end == name)
1014 return FAIL;
1015 size_t len = name_end - name;
1016
1017 class_T *cl = rettv->v_type == VAR_CLASS ? rettv->vval.v_class
1018 : rettv->vval.v_object->obj_class;
1019 if (*name_end == '(')
1020 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001021 int on_class = rettv->v_type == VAR_CLASS;
1022 int count = on_class ? cl->class_class_function_count
1023 : cl->class_obj_method_count;
1024 for (int i = 0; i < count; ++i)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001025 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001026 ufunc_T *fp = on_class ? cl->class_class_functions[i]
1027 : cl->class_obj_methods[i];
Bram Moolenaar4ae00572022-12-09 22:49:23 +00001028 // Use a separate pointer to avoid that ASAN complains about
1029 // uf_name[] only being 4 characters.
1030 char_u *ufname = (char_u *)fp->uf_name;
1031 if (STRNCMP(name, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001032 {
1033 typval_T argvars[MAX_FUNC_ARGS + 1];
1034 int argcount = 0;
1035
1036 char_u *argp = name_end;
1037 int ret = get_func_arguments(&argp, evalarg, 0,
1038 argvars, &argcount);
1039 if (ret == FAIL)
1040 return FAIL;
1041
1042 funcexe_T funcexe;
1043 CLEAR_FIELD(funcexe);
1044 funcexe.fe_evaluate = TRUE;
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001045 if (rettv->v_type == VAR_OBJECT)
1046 {
1047 funcexe.fe_object = rettv->vval.v_object;
1048 ++funcexe.fe_object->obj_refcount;
1049 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001050
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001051 // Clear the class or object after calling the function, in
1052 // case the refcount is one.
1053 typval_T tv_tofree = *rettv;
1054 rettv->v_type = VAR_UNKNOWN;
1055
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001056 // Call the user function. Result goes into rettv;
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001057 int error = call_user_func_check(fp, argcount, argvars,
1058 rettv, &funcexe, NULL);
1059
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001060 // Clear the previous rettv and the arguments.
1061 clear_tv(&tv_tofree);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001062 for (int idx = 0; idx < argcount; ++idx)
1063 clear_tv(&argvars[idx]);
1064
1065 if (error != FCERR_NONE)
1066 {
1067 user_func_error(error, printable_func_name(fp),
1068 funcexe.fe_found_var);
1069 return FAIL;
1070 }
1071 *arg = argp;
1072 return OK;
1073 }
1074 }
1075
1076 semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name);
1077 }
1078
1079 else if (rettv->v_type == VAR_OBJECT)
1080 {
1081 for (int i = 0; i < cl->class_obj_member_count; ++i)
1082 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001083 ocmember_T *m = &cl->class_obj_members[i];
1084 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001085 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001086 if (*name == '_')
1087 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001088 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
Bram Moolenaar3d473ee2022-12-14 20:59:32 +00001089 return FAIL;
1090 }
1091
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001092 // The object only contains a pointer to the class, the member
1093 // values array follows right after that.
1094 object_T *obj = rettv->vval.v_object;
1095 typval_T *tv = (typval_T *)(obj + 1) + i;
1096 copy_tv(tv, rettv);
1097 object_unref(obj);
1098
1099 *arg = name_end;
1100 return OK;
1101 }
1102 }
1103
1104 semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
1105 }
1106
Bram Moolenaard505d172022-12-18 21:42:55 +00001107 else if (rettv->v_type == VAR_CLASS)
1108 {
1109 // class member
1110 for (int i = 0; i < cl->class_class_member_count; ++i)
1111 {
1112 ocmember_T *m = &cl->class_class_members[i];
1113 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
1114 {
1115 if (*name == '_')
1116 {
1117 semsg(_(e_cannot_access_private_member_str), m->ocm_name);
1118 return FAIL;
1119 }
1120
1121 typval_T *tv = &cl->class_members_tv[i];
1122 copy_tv(tv, rettv);
1123 class_unref(cl);
1124
1125 *arg = name_end;
1126 return OK;
1127 }
1128 }
1129
1130 semsg(_(e_member_not_found_on_class_str_str), cl->class_name, name);
1131 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001132
1133 return FAIL;
1134}
1135
1136/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001137 * If "arg" points to a class or object method, return it.
1138 * Otherwise return NULL.
1139 */
1140 ufunc_T *
1141find_class_func(char_u **arg)
1142{
1143 char_u *name = *arg;
1144 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
1145 if (name_end == name || *name_end != '.')
1146 return NULL;
1147
1148 size_t len = name_end - name;
1149 typval_T tv;
1150 tv.v_type = VAR_UNKNOWN;
Bram Moolenaar993dbc32023-01-01 20:31:30 +00001151 if (eval_variable(name, (int)len,
1152 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001153 return NULL;
1154 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +00001155 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001156
1157 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
1158 : tv.vval.v_object->obj_class;
1159 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +00001160 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001161 char_u *fname = name_end + 1;
1162 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
1163 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +00001164 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001165 len = fname_end - fname;
1166
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001167 int count = tv.v_type == VAR_CLASS ? cl->class_class_function_count
1168 : cl->class_obj_method_count;
1169 ufunc_T **funcs = tv.v_type == VAR_CLASS ? cl->class_class_functions
1170 : cl->class_obj_methods;
1171 for (int i = 0; i < count; ++i)
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001172 {
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001173 ufunc_T *fp = funcs[i];
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001174 // Use a separate pointer to avoid that ASAN complains about
1175 // uf_name[] only being 4 characters.
1176 char_u *ufname = (char_u *)fp->uf_name;
1177 if (STRNCMP(fname, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaareb533502022-12-14 15:06:11 +00001178 {
1179 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001180 return fp;
Bram Moolenaareb533502022-12-14 15:06:11 +00001181 }
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001182 }
1183
Bram Moolenaareb533502022-12-14 15:06:11 +00001184fail_after_eval:
1185 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +00001186 return NULL;
1187}
1188
1189/*
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001190 * If "name[len]" is a class member in cctx->ctx_ufunc->uf_class return the
1191 * index in class.class_class_members[].
1192 * If "cl_ret" is not NULL set it to the class.
1193 * Otherwise return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001194 */
1195 int
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001196class_member_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001197{
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001198 if (cctx == NULL || cctx->ctx_ufunc == NULL
1199 || cctx->ctx_ufunc->uf_class == NULL)
1200 return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001201 class_T *cl = cctx->ctx_ufunc->uf_class;
1202
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001203 for (int i = 0; i < cl->class_class_member_count; ++i)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001204 {
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001205 ocmember_T *m = &cl->class_class_members[i];
1206 if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001207 {
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001208 if (cl_ret != NULL)
1209 *cl_ret = cl;
1210 return i;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001211 }
1212 }
Bram Moolenaar6acf7572023-01-01 19:53:30 +00001213 return -1;
Bram Moolenaar6bafdd42023-01-01 12:58:33 +00001214}
1215
1216/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001217 * Make a copy of an object.
1218 */
1219 void
1220copy_object(typval_T *from, typval_T *to)
1221{
1222 *to = *from;
1223 if (to->vval.v_object != NULL)
1224 ++to->vval.v_object->obj_refcount;
1225}
1226
1227/*
1228 * Free an object.
1229 */
1230 static void
1231object_clear(object_T *obj)
1232{
1233 class_T *cl = obj->obj_class;
1234
1235 // the member values are just after the object structure
1236 typval_T *tv = (typval_T *)(obj + 1);
1237 for (int i = 0; i < cl->class_obj_member_count; ++i)
1238 clear_tv(tv + i);
1239
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001240 // Remove from the list headed by "first_object".
1241 object_cleared(obj);
1242
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001243 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001244 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001245}
1246
1247/*
1248 * Unreference an object.
1249 */
1250 void
1251object_unref(object_T *obj)
1252{
1253 if (obj != NULL && --obj->obj_refcount <= 0)
1254 object_clear(obj);
1255}
1256
1257/*
1258 * Make a copy of a class.
1259 */
1260 void
1261copy_class(typval_T *from, typval_T *to)
1262{
1263 *to = *from;
1264 if (to->vval.v_class != NULL)
1265 ++to->vval.v_class->class_refcount;
1266}
1267
1268/*
1269 * Unreference a class. Free it when the reference count goes down to zero.
1270 */
1271 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001272class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001273{
Bram Moolenaard505d172022-12-18 21:42:55 +00001274 if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001275 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001276 // Freeing what the class contains may recursively come back here.
1277 // Clear "class_name" first, if it is NULL the class does not need to
1278 // be freed.
1279 VIM_CLEAR(cl->class_name);
1280
Bram Moolenaar83677162023-01-08 19:54:10 +00001281 class_unref(cl->class_extends);
1282
Bram Moolenaar94674f22023-01-06 18:42:20 +00001283 for (int i = 0; i < cl->class_interface_count; ++i)
1284 vim_free(((char_u **)cl->class_interfaces)[i]);
1285 vim_free(cl->class_interfaces);
1286
Bram Moolenaard505d172022-12-18 21:42:55 +00001287 for (int i = 0; i < cl->class_class_member_count; ++i)
1288 {
1289 ocmember_T *m = &cl->class_class_members[i];
1290 vim_free(m->ocm_name);
1291 vim_free(m->ocm_init);
1292 if (cl->class_members_tv != NULL)
1293 clear_tv(&cl->class_members_tv[i]);
1294 }
1295 vim_free(cl->class_class_members);
1296 vim_free(cl->class_members_tv);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001297
1298 for (int i = 0; i < cl->class_obj_member_count; ++i)
1299 {
Bram Moolenaard505d172022-12-18 21:42:55 +00001300 ocmember_T *m = &cl->class_obj_members[i];
1301 vim_free(m->ocm_name);
1302 vim_free(m->ocm_init);
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001303 }
1304 vim_free(cl->class_obj_members);
1305
Bram Moolenaarec8b74f2023-01-01 14:11:27 +00001306 for (int i = 0; i < cl->class_class_function_count; ++i)
1307 {
1308 ufunc_T *uf = cl->class_class_functions[i];
1309 func_clear_free(uf, FALSE);
1310 }
1311 vim_free(cl->class_class_functions);
1312
Bram Moolenaarffdaca92022-12-09 21:41:48 +00001313 for (int i = 0; i < cl->class_obj_method_count; ++i)
1314 {
1315 ufunc_T *uf = cl->class_obj_methods[i];
1316 func_clear_free(uf, FALSE);
1317 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001318 vim_free(cl->class_obj_methods);
1319
Bram Moolenaar00b28d62022-12-08 15:32:33 +00001320 clear_type_list(&cl->class_type_list);
1321
1322 vim_free(cl);
1323 }
1324}
1325
Bram Moolenaard28d7b92022-12-08 20:42:00 +00001326static object_T *first_object = NULL;
1327
1328/*
1329 * Call this function when an object has been created. It will be added to the
1330 * list headed by "first_object".
1331 */
1332 void
1333object_created(object_T *obj)
1334{
1335 if (first_object != NULL)
1336 {
1337 obj->obj_next_used = first_object;
1338 first_object->obj_prev_used = obj;
1339 }
1340 first_object = obj;
1341}
1342
1343/*
1344 * Call this function when an object has been cleared and is about to be freed.
1345 * It is removed from the list headed by "first_object".
1346 */
1347 void
1348object_cleared(object_T *obj)
1349{
1350 if (obj->obj_next_used != NULL)
1351 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
1352 if (obj->obj_prev_used != NULL)
1353 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
1354 else if (first_object == obj)
1355 first_object = obj->obj_next_used;
1356}
1357
1358/*
1359 * Go through the list of all objects and free items without "copyID".
1360 */
1361 int
1362object_free_nonref(int copyID)
1363{
1364 int did_free = FALSE;
1365 object_T *next_obj;
1366
1367 for (object_T *obj = first_object; obj != NULL; obj = next_obj)
1368 {
1369 next_obj = obj->obj_next_used;
1370 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
1371 {
1372 // Free the object and items it contains.
1373 object_clear(obj);
1374 did_free = TRUE;
1375 }
1376 }
1377
1378 return did_free;
1379}
1380
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001381
1382#endif // FEAT_EVAL