blob: da44c3ddf451e2f920c753a442f165d64fa7adea [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/*
25 * Handle ":class" and ":abstract class" up to ":endclass".
26 */
27 void
28ex_class(exarg_T *eap)
29{
Bram Moolenaar00b28d62022-12-08 15:32:33 +000030 if (!current_script_is_vim9()
31 || (cmdmod.cmod_flags & CMOD_LEGACY)
32 || !getline_equal(eap->getline, eap->cookie, getsourceline))
33 {
34 emsg(_(e_class_can_only_be_defined_in_vim9_script));
35 return;
36 }
Bram Moolenaarc1c365c2022-12-04 20:13:24 +000037
38 char_u *arg = eap->arg;
Bram Moolenaar00b28d62022-12-08 15:32:33 +000039 int is_abstract = eap->cmdidx == CMD_abstract;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +000040 if (is_abstract)
41 {
42 if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
43 {
44 semsg(_(e_invalid_argument_str), arg);
45 return;
46 }
47 arg = skipwhite(arg + 5);
48 }
49
50 if (!ASCII_ISUPPER(*arg))
51 {
52 semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
53 return;
54 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +000055 char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
56 if (!IS_WHITE_OR_NUL(*name_end))
57 {
58 semsg(_(e_white_space_required_after_class_name_str), arg);
59 return;
60 }
Bram Moolenaarc1c365c2022-12-04 20:13:24 +000061
62 // TODO:
Bram Moolenaar00b28d62022-12-08 15:32:33 +000063 // generics: <Tkey, Tentry>
Bram Moolenaarc1c365c2022-12-04 20:13:24 +000064 // extends SomeClass
65 // implements SomeInterface
66 // specifies SomeInterface
Bram Moolenaar00b28d62022-12-08 15:32:33 +000067 // check nothing follows
Bram Moolenaarc1c365c2022-12-04 20:13:24 +000068
Bram Moolenaar00b28d62022-12-08 15:32:33 +000069 // TODO: handle "is_export" if it is set
Bram Moolenaarc1c365c2022-12-04 20:13:24 +000070
Bram Moolenaar00b28d62022-12-08 15:32:33 +000071 garray_T type_list; // list of pointers to allocated types
72 ga_init2(&type_list, sizeof(type_T *), 10);
73
74 // Growarray with object members declared in the class.
75 garray_T objmembers;
76 ga_init2(&objmembers, sizeof(objmember_T), 10);
77
78 // Growarray with object methods declared in the class.
79 garray_T objmethods;
Bram Moolenaarffdaca92022-12-09 21:41:48 +000080 ga_init2(&objmethods, sizeof(ufunc_T *), 10);
Bram Moolenaar00b28d62022-12-08 15:32:33 +000081
82 /*
83 * Go over the body of the class until "endclass" is found.
84 */
85 char_u *theline = NULL;
86 int success = FALSE;
87 for (;;)
88 {
89 vim_free(theline);
90 theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
91 if (theline == NULL)
92 break;
93 char_u *line = skipwhite(theline);
94
95 // TODO:
96 // class members (public, read access, private):
97 // static varname
98 // public static varname
99 // static _varname
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000100
101 char_u *p = line;
102 if (checkforcmd(&p, "endclass", 4))
103 {
104 if (STRNCMP(line, "endclass", 8) != 0)
105 semsg(_(e_command_cannot_be_shortened_str), line);
106 else if (*p == '|' || !ends_excmd2(line, p))
107 semsg(_(e_trailing_characters_str), p);
Bram Moolenaar98aeb212022-12-08 22:09:14 +0000108 else
109 success = TRUE;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000110 break;
111 }
112
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000113 // "this._varname"
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000114 // "this.varname"
115 // "public this.varname"
116 int has_public = FALSE;
117 if (checkforcmd(&p, "public", 3))
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000118 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000119 if (STRNCMP(line, "public", 6) != 0)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000120 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000121 semsg(_(e_command_cannot_be_shortened_str), line);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000122 break;
123 }
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000124 has_public = TRUE;
125 p = skipwhite(line + 6);
126
127 if (STRNCMP(p, "this", 4) != 0)
128 {
129 emsg(_(e_public_must_be_followed_by_this));
130 break;
131 }
132 }
133 if (STRNCMP(p, "this", 4) == 0)
134 {
135 if (p[4] != '.' || !eval_isnamec1(p[5]))
136 {
137 semsg(_(e_invalid_object_member_declaration_str), p);
138 break;
139 }
140 char_u *varname = p + 5;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000141 char_u *varname_end = to_name_end(varname, FALSE);
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000142 if (*varname == '_' && has_public)
143 {
144 semsg(_(e_public_object_member_name_cannot_start_with_underscore_str), line);
145 break;
146 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000147
148 char_u *colon = skipwhite(varname_end);
Bram Moolenaar74e12742022-12-13 21:14:28 +0000149 char_u *type_arg = colon;
150 type_T *type = NULL;
151 if (*colon == ':')
152 {
153 if (VIM_ISWHITE(*varname_end))
154 {
155 semsg(_(e_no_white_space_allowed_before_colon_str),
156 varname);
157 break;
158 }
159 if (!VIM_ISWHITE(colon[1]))
160 {
161 semsg(_(e_white_space_required_after_str_str), ":",
162 varname);
163 break;
164 }
165 type_arg = skipwhite(colon + 1);
166 type = parse_type(&type_arg, &type_list, TRUE);
167 if (type == NULL)
168 break;
169 }
170
171 char_u *expr_start = skipwhite(type_arg);
172 char_u *expr_end = expr_start;
173 if (type == NULL && *expr_start != '=')
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000174 {
175 emsg(_(e_type_or_initialization_required));
176 break;
177 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000178
Bram Moolenaar74e12742022-12-13 21:14:28 +0000179 if (*expr_start == '=')
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000180 {
Bram Moolenaar74e12742022-12-13 21:14:28 +0000181 if (!VIM_ISWHITE(expr_start[-1]) || !VIM_ISWHITE(expr_start[1]))
182 {
183 semsg(_(e_white_space_required_before_and_after_str_at_str),
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000184 "=", type_arg);
Bram Moolenaar74e12742022-12-13 21:14:28 +0000185 break;
186 }
187 expr_start = skipwhite(expr_start + 1);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000188
Bram Moolenaar74e12742022-12-13 21:14:28 +0000189 expr_end = expr_start;
190 evalarg_T evalarg;
191 fill_evalarg_from_eap(&evalarg, eap, FALSE);
Bram Moolenaareb533502022-12-14 15:06:11 +0000192 skip_expr(&expr_end, NULL);
Bram Moolenaar74e12742022-12-13 21:14:28 +0000193
194 if (type == NULL)
195 {
196 // No type specified, use the type of the initializer.
197 typval_T tv;
198 tv.v_type = VAR_UNKNOWN;
199 char_u *expr = expr_start;
200 int res = eval0(expr, &tv, eap, &evalarg);
201
202 if (res == OK)
203 type = typval2type(&tv, get_copyID(), &type_list,
204 TVTT_DO_MEMBER);
205 if (type == NULL)
206 {
207 semsg(_(e_cannot_get_object_member_type_from_initializer_str),
208 expr_start);
209 clear_evalarg(&evalarg, NULL);
210 break;
211 }
212 }
213 clear_evalarg(&evalarg, NULL);
214 }
215 if (!valid_declaration_type(type))
216 break;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000217
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000218 if (ga_grow(&objmembers, 1) == FAIL)
219 break;
220 objmember_T *m = ((objmember_T *)objmembers.ga_data)
221 + objmembers.ga_len;
222 m->om_name = vim_strnsave(varname, varname_end - varname);
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000223 m->om_access = has_public ? ACCESS_ALL
224 : *varname == '_' ? ACCESS_PRIVATE
225 : ACCESS_READ;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000226 m->om_type = type;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000227 if (expr_end > expr_start)
228 m->om_init = vim_strnsave(expr_start, expr_end - expr_start);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000229 ++objmembers.ga_len;
230 }
231
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000232 // constructors:
233 // def new()
234 // enddef
235 // def newOther()
236 // enddef
237 // methods:
238 // def someMethod()
239 // enddef
240 // TODO:
241 // static def someMethod()
242 // enddef
243 // def <Tval> someMethod()
244 // enddef
245 // static def <Tval> someMethod()
246 // enddef
247 else if (checkforcmd(&p, "def", 3))
248 {
249 exarg_T ea;
250 garray_T lines_to_free;
251
252 CLEAR_FIELD(ea);
253 ea.cmd = line;
254 ea.arg = p;
255 ea.cmdidx = CMD_def;
256 ea.getline = eap->getline;
257 ea.cookie = eap->cookie;
258
259 ga_init2(&lines_to_free, sizeof(char_u *), 50);
260 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free, TRUE);
261 ga_clear_strings(&lines_to_free);
262
263 // TODO: how about errors?
264 if (uf != NULL && ga_grow(&objmethods, 1) == OK)
265 {
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000266 if (STRNCMP(uf->uf_name, "new", 3) == 0)
267 uf->uf_flags |= FC_NEW;
268
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000269 ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = uf;
270 ++objmethods.ga_len;
271 }
272 }
273
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000274 else
275 {
276 semsg(_(e_not_valid_command_in_class_str), line);
277 break;
278 }
279 }
280 vim_free(theline);
281
Bram Moolenaareb533502022-12-14 15:06:11 +0000282 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000283 if (success)
284 {
Bram Moolenaareb533502022-12-14 15:06:11 +0000285 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000286 if (cl == NULL)
287 goto cleanup;
288 cl->class_refcount = 1;
289 cl->class_name = vim_strnsave(arg, name_end - arg);
290
291 // Members are used by the new() function, add them here.
292 cl->class_obj_member_count = objmembers.ga_len;
Bram Moolenaar98aeb212022-12-08 22:09:14 +0000293 cl->class_obj_members = objmembers.ga_len == 0 ? NULL
294 : ALLOC_MULT(objmember_T, objmembers.ga_len);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000295 if (cl->class_name == NULL
Bram Moolenaar98aeb212022-12-08 22:09:14 +0000296 || (objmembers.ga_len > 0 && cl->class_obj_members == NULL))
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000297 goto cleanup;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000298 mch_memmove(cl->class_obj_members, objmembers.ga_data,
299 sizeof(objmember_T) * objmembers.ga_len);
300 vim_free(objmembers.ga_data);
301
302 int have_new = FALSE;
303 for (int i = 0; i < objmethods.ga_len; ++i)
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000304 if (STRCMP(((ufunc_T **)objmethods.ga_data)[i]->uf_name,
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000305 "new") == 0)
306 {
307 have_new = TRUE;
308 break;
309 }
310 if (!have_new)
311 {
312 // No new() method was defined, add the default constructor.
313 garray_T fga;
314 ga_init2(&fga, 1, 1000);
315 ga_concat(&fga, (char_u *)"new(");
316 for (int i = 0; i < cl->class_obj_member_count; ++i)
317 {
318 if (i > 0)
319 ga_concat(&fga, (char_u *)", ");
320 ga_concat(&fga, (char_u *)"this.");
321 objmember_T *m = cl->class_obj_members + i;
322 ga_concat(&fga, (char_u *)m->om_name);
Bram Moolenaar65b0d162022-12-13 18:43:22 +0000323 ga_concat(&fga, (char_u *)" = v:none");
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000324 }
325 ga_concat(&fga, (char_u *)")\nenddef\n");
326 ga_append(&fga, NUL);
327
328 exarg_T fea;
329 CLEAR_FIELD(fea);
330 fea.cmdidx = CMD_def;
331 fea.cmd = fea.arg = fga.ga_data;
332
333 garray_T lines_to_free;
334 ga_init2(&lines_to_free, sizeof(char_u *), 50);
335
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000336 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, TRUE);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000337
338 ga_clear_strings(&lines_to_free);
339 vim_free(fga.ga_data);
340
341 if (nf != NULL && ga_grow(&objmethods, 1) == OK)
342 {
343 ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = nf;
344 ++objmethods.ga_len;
345
346 nf->uf_flags |= FC_NEW;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000347 nf->uf_ret_type = get_type_ptr(&type_list);
348 if (nf->uf_ret_type != NULL)
349 {
350 nf->uf_ret_type->tt_type = VAR_OBJECT;
351 nf->uf_ret_type->tt_member = (type_T *)cl;
352 nf->uf_ret_type->tt_argcount = 0;
353 nf->uf_ret_type->tt_args = NULL;
354 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000355 }
356 }
357
358 cl->class_obj_method_count = objmethods.ga_len;
359 cl->class_obj_methods = ALLOC_MULT(ufunc_T *, objmethods.ga_len);
360 if (cl->class_obj_methods == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000361 goto cleanup;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000362 mch_memmove(cl->class_obj_methods, objmethods.ga_data,
363 sizeof(ufunc_T *) * objmethods.ga_len);
364 vim_free(objmethods.ga_data);
365
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000366 // Set the class pointer on all the object methods.
367 for (int i = 0; i < objmethods.ga_len; ++i)
368 {
369 ufunc_T *fp = cl->class_obj_methods[i];
370 fp->uf_class = cl;
371 fp->uf_flags |= FC_OBJECT; // TODO: not for class method
372 }
373
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000374 cl->class_type.tt_type = VAR_CLASS;
375 cl->class_type.tt_member = (type_T *)cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000376 cl->class_object_type.tt_type = VAR_OBJECT;
377 cl->class_object_type.tt_member = (type_T *)cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000378 cl->class_type_list = type_list;
379
380 // TODO:
381 // - Add the methods to the class
382 // - array with ufunc_T pointers
383 // - Fill hashtab with object members and methods
384 // - Generate the default new() method, if needed.
385 // Later:
386 // - class members
387 // - class methods
388
389 // Add the class to the script-local variables.
390 typval_T tv;
391 tv.v_type = VAR_CLASS;
392 tv.vval.v_class = cl;
393 set_var_const(cl->class_name, current_sctx.sc_sid,
394 NULL, &tv, FALSE, ASSIGN_DECL, 0);
395 return;
396 }
397
398cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +0000399 if (cl != NULL)
400 {
401 vim_free(cl->class_name);
402 vim_free(cl->class_obj_members);
403 vim_free(cl->class_obj_methods);
404 vim_free(cl);
405 }
406
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000407 for (int i = 0; i < objmembers.ga_len; ++i)
408 {
409 objmember_T *m = ((objmember_T *)objmembers.ga_data) + i;
410 vim_free(m->om_name);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000411 vim_free(m->om_init);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000412 }
413 ga_clear(&objmembers);
414
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000415 for (int i = 0; i < objmethods.ga_len; ++i)
416 {
417 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
418 func_clear_free(uf, FALSE);
419 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000420 ga_clear(&objmethods);
421 clear_type_list(&type_list);
422}
423
424/*
425 * Find member "name" in class "cl" and return its type.
426 * When not found t_any is returned.
427 */
428 type_T *
429class_member_type(
430 class_T *cl,
431 char_u *name,
432 char_u *name_end,
433 int *member_idx)
434{
435 *member_idx = -1; // not found (yet)
436 size_t len = name_end - name;
437
438 for (int i = 0; i < cl->class_obj_member_count; ++i)
439 {
440 objmember_T *m = cl->class_obj_members + i;
441 if (STRNCMP(m->om_name, name, len) == 0 && m->om_name[len] == NUL)
442 {
443 *member_idx = i;
444 return m->om_type;
445 }
446 }
447 return &t_any;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000448}
449
450/*
451 * Handle ":interface" up to ":endinterface".
452 */
453 void
454ex_interface(exarg_T *eap UNUSED)
455{
456 // TODO
457}
458
459/*
460 * Handle ":enum" up to ":endenum".
461 */
462 void
463ex_enum(exarg_T *eap UNUSED)
464{
465 // TODO
466}
467
468/*
469 * Handle ":type".
470 */
471 void
472ex_type(exarg_T *eap UNUSED)
473{
474 // TODO
475}
476
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000477/*
478 * Evaluate what comes after a class:
479 * - class member: SomeClass.varname
480 * - class method: SomeClass.SomeMethod()
481 * - class constructor: SomeClass.new()
482 * - object member: someObject.varname
483 * - object method: someObject.SomeMethod()
484 *
485 * "*arg" points to the '.'.
486 * "*arg" is advanced to after the member name or method call.
487 *
488 * Returns FAIL or OK.
489 */
490 int
491class_object_index(
492 char_u **arg,
493 typval_T *rettv,
494 evalarg_T *evalarg,
495 int verbose UNUSED) // give error messages
496{
497 // int evaluate = evalarg != NULL
498 // && (evalarg->eval_flags & EVAL_EVALUATE);
499
500 if (VIM_ISWHITE((*arg)[1]))
501 {
502 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
503 return FAIL;
504 }
505
506 ++*arg;
507 char_u *name = *arg;
508 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
509 if (name_end == name)
510 return FAIL;
511 size_t len = name_end - name;
512
513 class_T *cl = rettv->v_type == VAR_CLASS ? rettv->vval.v_class
514 : rettv->vval.v_object->obj_class;
515 if (*name_end == '(')
516 {
517 for (int i = 0; i < cl->class_obj_method_count; ++i)
518 {
519 ufunc_T *fp = cl->class_obj_methods[i];
Bram Moolenaar4ae00572022-12-09 22:49:23 +0000520 // Use a separate pointer to avoid that ASAN complains about
521 // uf_name[] only being 4 characters.
522 char_u *ufname = (char_u *)fp->uf_name;
523 if (STRNCMP(name, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000524 {
525 typval_T argvars[MAX_FUNC_ARGS + 1];
526 int argcount = 0;
527
528 char_u *argp = name_end;
529 int ret = get_func_arguments(&argp, evalarg, 0,
530 argvars, &argcount);
531 if (ret == FAIL)
532 return FAIL;
533
534 funcexe_T funcexe;
535 CLEAR_FIELD(funcexe);
536 funcexe.fe_evaluate = TRUE;
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000537 if (rettv->v_type == VAR_OBJECT)
538 {
539 funcexe.fe_object = rettv->vval.v_object;
540 ++funcexe.fe_object->obj_refcount;
541 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000542
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000543 // Clear the class or object after calling the function, in
544 // case the refcount is one.
545 typval_T tv_tofree = *rettv;
546 rettv->v_type = VAR_UNKNOWN;
547
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000548 // Call the user function. Result goes into rettv;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000549 int error = call_user_func_check(fp, argcount, argvars,
550 rettv, &funcexe, NULL);
551
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000552 // Clear the previous rettv and the arguments.
553 clear_tv(&tv_tofree);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000554 for (int idx = 0; idx < argcount; ++idx)
555 clear_tv(&argvars[idx]);
556
557 if (error != FCERR_NONE)
558 {
559 user_func_error(error, printable_func_name(fp),
560 funcexe.fe_found_var);
561 return FAIL;
562 }
563 *arg = argp;
564 return OK;
565 }
566 }
567
568 semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name);
569 }
570
571 else if (rettv->v_type == VAR_OBJECT)
572 {
573 for (int i = 0; i < cl->class_obj_member_count; ++i)
574 {
575 objmember_T *m = &cl->class_obj_members[i];
576 if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL)
577 {
Bram Moolenaar3d473ee2022-12-14 20:59:32 +0000578 if (*name == '_')
579 {
580 semsg(_(e_cannot_access_private_object_member_str),
581 m->om_name);
582 return FAIL;
583 }
584
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000585 // The object only contains a pointer to the class, the member
586 // values array follows right after that.
587 object_T *obj = rettv->vval.v_object;
588 typval_T *tv = (typval_T *)(obj + 1) + i;
589 copy_tv(tv, rettv);
590 object_unref(obj);
591
592 *arg = name_end;
593 return OK;
594 }
595 }
596
597 semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
598 }
599
600 // TODO: class member
601
602 return FAIL;
603}
604
605/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000606 * If "arg" points to a class or object method, return it.
607 * Otherwise return NULL.
608 */
609 ufunc_T *
610find_class_func(char_u **arg)
611{
612 char_u *name = *arg;
613 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
614 if (name_end == name || *name_end != '.')
615 return NULL;
616
617 size_t len = name_end - name;
618 typval_T tv;
619 tv.v_type = VAR_UNKNOWN;
620 if (eval_variable(name, len, 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
621 return NULL;
622 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +0000623 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000624
625 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
626 : tv.vval.v_object->obj_class;
627 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +0000628 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000629 char_u *fname = name_end + 1;
630 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
631 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +0000632 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000633 len = fname_end - fname;
634
635 for (int i = 0; i < cl->class_obj_method_count; ++i)
636 {
637 ufunc_T *fp = cl->class_obj_methods[i];
638 // Use a separate pointer to avoid that ASAN complains about
639 // uf_name[] only being 4 characters.
640 char_u *ufname = (char_u *)fp->uf_name;
641 if (STRNCMP(fname, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaareb533502022-12-14 15:06:11 +0000642 {
643 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000644 return fp;
Bram Moolenaareb533502022-12-14 15:06:11 +0000645 }
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000646 }
647
Bram Moolenaareb533502022-12-14 15:06:11 +0000648fail_after_eval:
649 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000650 return NULL;
651}
652
653/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000654 * Make a copy of an object.
655 */
656 void
657copy_object(typval_T *from, typval_T *to)
658{
659 *to = *from;
660 if (to->vval.v_object != NULL)
661 ++to->vval.v_object->obj_refcount;
662}
663
664/*
665 * Free an object.
666 */
667 static void
668object_clear(object_T *obj)
669{
670 class_T *cl = obj->obj_class;
671
672 // the member values are just after the object structure
673 typval_T *tv = (typval_T *)(obj + 1);
674 for (int i = 0; i < cl->class_obj_member_count; ++i)
675 clear_tv(tv + i);
676
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000677 // Remove from the list headed by "first_object".
678 object_cleared(obj);
679
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000680 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000681 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000682}
683
684/*
685 * Unreference an object.
686 */
687 void
688object_unref(object_T *obj)
689{
690 if (obj != NULL && --obj->obj_refcount <= 0)
691 object_clear(obj);
692}
693
694/*
695 * Make a copy of a class.
696 */
697 void
698copy_class(typval_T *from, typval_T *to)
699{
700 *to = *from;
701 if (to->vval.v_class != NULL)
702 ++to->vval.v_class->class_refcount;
703}
704
705/*
706 * Unreference a class. Free it when the reference count goes down to zero.
707 */
708 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000709class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000710{
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000711 if (cl != NULL && --cl->class_refcount <= 0)
712 {
713 vim_free(cl->class_name);
714
715 for (int i = 0; i < cl->class_obj_member_count; ++i)
716 {
717 objmember_T *m = &cl->class_obj_members[i];
718 vim_free(m->om_name);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000719 vim_free(m->om_init);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000720 }
721 vim_free(cl->class_obj_members);
722
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000723 for (int i = 0; i < cl->class_obj_method_count; ++i)
724 {
725 ufunc_T *uf = cl->class_obj_methods[i];
726 func_clear_free(uf, FALSE);
727 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000728 vim_free(cl->class_obj_methods);
729
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000730 clear_type_list(&cl->class_type_list);
731
732 vim_free(cl);
733 }
734}
735
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000736static object_T *first_object = NULL;
737
738/*
739 * Call this function when an object has been created. It will be added to the
740 * list headed by "first_object".
741 */
742 void
743object_created(object_T *obj)
744{
745 if (first_object != NULL)
746 {
747 obj->obj_next_used = first_object;
748 first_object->obj_prev_used = obj;
749 }
750 first_object = obj;
751}
752
753/*
754 * Call this function when an object has been cleared and is about to be freed.
755 * It is removed from the list headed by "first_object".
756 */
757 void
758object_cleared(object_T *obj)
759{
760 if (obj->obj_next_used != NULL)
761 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
762 if (obj->obj_prev_used != NULL)
763 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
764 else if (first_object == obj)
765 first_object = obj->obj_next_used;
766}
767
768/*
769 * Go through the list of all objects and free items without "copyID".
770 */
771 int
772object_free_nonref(int copyID)
773{
774 int did_free = FALSE;
775 object_T *next_obj;
776
777 for (object_T *obj = first_object; obj != NULL; obj = next_obj)
778 {
779 next_obj = obj->obj_next_used;
780 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
781 {
782 // Free the object and items it contains.
783 object_clear(obj);
784 did_free = TRUE;
785 }
786 }
787
788 return did_free;
789}
790
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000791
792#endif // FEAT_EVAL