blob: 86327b499e8b798eea33f48c01fabe35f93281b4 [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
113 // "this.varname"
114 // "this._varname"
115 // TODO:
116 // "public this.varname"
117 if (STRNCMP(line, "this", 4) == 0)
118 {
119 if (line[4] != '.' || !eval_isnamec1(line[5]))
120 {
121 semsg(_(e_invalid_object_member_declaration_str), line);
122 break;
123 }
124 char_u *varname = line + 5;
125 char_u *varname_end = to_name_end(varname, FALSE);
126
127 char_u *colon = skipwhite(varname_end);
Bram Moolenaar74e12742022-12-13 21:14:28 +0000128 char_u *type_arg = colon;
129 type_T *type = NULL;
130 if (*colon == ':')
131 {
132 if (VIM_ISWHITE(*varname_end))
133 {
134 semsg(_(e_no_white_space_allowed_before_colon_str),
135 varname);
136 break;
137 }
138 if (!VIM_ISWHITE(colon[1]))
139 {
140 semsg(_(e_white_space_required_after_str_str), ":",
141 varname);
142 break;
143 }
144 type_arg = skipwhite(colon + 1);
145 type = parse_type(&type_arg, &type_list, TRUE);
146 if (type == NULL)
147 break;
148 }
149
150 char_u *expr_start = skipwhite(type_arg);
151 char_u *expr_end = expr_start;
152 if (type == NULL && *expr_start != '=')
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000153 {
154 emsg(_(e_type_or_initialization_required));
155 break;
156 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000157
Bram Moolenaar74e12742022-12-13 21:14:28 +0000158 if (*expr_start == '=')
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000159 {
Bram Moolenaar74e12742022-12-13 21:14:28 +0000160 if (!VIM_ISWHITE(expr_start[-1]) || !VIM_ISWHITE(expr_start[1]))
161 {
162 semsg(_(e_white_space_required_before_and_after_str_at_str),
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000163 "=", type_arg);
Bram Moolenaar74e12742022-12-13 21:14:28 +0000164 break;
165 }
166 expr_start = skipwhite(expr_start + 1);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000167
Bram Moolenaar74e12742022-12-13 21:14:28 +0000168 expr_end = expr_start;
169 evalarg_T evalarg;
170 fill_evalarg_from_eap(&evalarg, eap, FALSE);
Bram Moolenaareb533502022-12-14 15:06:11 +0000171 skip_expr(&expr_end, NULL);
Bram Moolenaar74e12742022-12-13 21:14:28 +0000172
173 if (type == NULL)
174 {
175 // No type specified, use the type of the initializer.
176 typval_T tv;
177 tv.v_type = VAR_UNKNOWN;
178 char_u *expr = expr_start;
179 int res = eval0(expr, &tv, eap, &evalarg);
180
181 if (res == OK)
182 type = typval2type(&tv, get_copyID(), &type_list,
183 TVTT_DO_MEMBER);
184 if (type == NULL)
185 {
186 semsg(_(e_cannot_get_object_member_type_from_initializer_str),
187 expr_start);
188 clear_evalarg(&evalarg, NULL);
189 break;
190 }
191 }
192 clear_evalarg(&evalarg, NULL);
193 }
194 if (!valid_declaration_type(type))
195 break;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000196
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000197 if (ga_grow(&objmembers, 1) == FAIL)
198 break;
199 objmember_T *m = ((objmember_T *)objmembers.ga_data)
200 + objmembers.ga_len;
201 m->om_name = vim_strnsave(varname, varname_end - varname);
202 m->om_type = type;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000203 if (expr_end > expr_start)
204 m->om_init = vim_strnsave(expr_start, expr_end - expr_start);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000205 ++objmembers.ga_len;
206 }
207
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000208 // constructors:
209 // def new()
210 // enddef
211 // def newOther()
212 // enddef
213 // methods:
214 // def someMethod()
215 // enddef
216 // TODO:
217 // static def someMethod()
218 // enddef
219 // def <Tval> someMethod()
220 // enddef
221 // static def <Tval> someMethod()
222 // enddef
223 else if (checkforcmd(&p, "def", 3))
224 {
225 exarg_T ea;
226 garray_T lines_to_free;
227
228 CLEAR_FIELD(ea);
229 ea.cmd = line;
230 ea.arg = p;
231 ea.cmdidx = CMD_def;
232 ea.getline = eap->getline;
233 ea.cookie = eap->cookie;
234
235 ga_init2(&lines_to_free, sizeof(char_u *), 50);
236 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free, TRUE);
237 ga_clear_strings(&lines_to_free);
238
239 // TODO: how about errors?
240 if (uf != NULL && ga_grow(&objmethods, 1) == OK)
241 {
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000242 if (STRNCMP(uf->uf_name, "new", 3) == 0)
243 uf->uf_flags |= FC_NEW;
244
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000245 ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = uf;
246 ++objmethods.ga_len;
247 }
248 }
249
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000250 else
251 {
252 semsg(_(e_not_valid_command_in_class_str), line);
253 break;
254 }
255 }
256 vim_free(theline);
257
Bram Moolenaareb533502022-12-14 15:06:11 +0000258 class_T *cl = NULL;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000259 if (success)
260 {
Bram Moolenaareb533502022-12-14 15:06:11 +0000261 cl = ALLOC_CLEAR_ONE(class_T);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000262 if (cl == NULL)
263 goto cleanup;
264 cl->class_refcount = 1;
265 cl->class_name = vim_strnsave(arg, name_end - arg);
266
267 // Members are used by the new() function, add them here.
268 cl->class_obj_member_count = objmembers.ga_len;
Bram Moolenaar98aeb212022-12-08 22:09:14 +0000269 cl->class_obj_members = objmembers.ga_len == 0 ? NULL
270 : ALLOC_MULT(objmember_T, objmembers.ga_len);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000271 if (cl->class_name == NULL
Bram Moolenaar98aeb212022-12-08 22:09:14 +0000272 || (objmembers.ga_len > 0 && cl->class_obj_members == NULL))
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000273 goto cleanup;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000274 mch_memmove(cl->class_obj_members, objmembers.ga_data,
275 sizeof(objmember_T) * objmembers.ga_len);
276 vim_free(objmembers.ga_data);
277
278 int have_new = FALSE;
279 for (int i = 0; i < objmethods.ga_len; ++i)
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000280 if (STRCMP(((ufunc_T **)objmethods.ga_data)[i]->uf_name,
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000281 "new") == 0)
282 {
283 have_new = TRUE;
284 break;
285 }
286 if (!have_new)
287 {
288 // No new() method was defined, add the default constructor.
289 garray_T fga;
290 ga_init2(&fga, 1, 1000);
291 ga_concat(&fga, (char_u *)"new(");
292 for (int i = 0; i < cl->class_obj_member_count; ++i)
293 {
294 if (i > 0)
295 ga_concat(&fga, (char_u *)", ");
296 ga_concat(&fga, (char_u *)"this.");
297 objmember_T *m = cl->class_obj_members + i;
298 ga_concat(&fga, (char_u *)m->om_name);
Bram Moolenaar65b0d162022-12-13 18:43:22 +0000299 ga_concat(&fga, (char_u *)" = v:none");
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000300 }
301 ga_concat(&fga, (char_u *)")\nenddef\n");
302 ga_append(&fga, NUL);
303
304 exarg_T fea;
305 CLEAR_FIELD(fea);
306 fea.cmdidx = CMD_def;
307 fea.cmd = fea.arg = fga.ga_data;
308
309 garray_T lines_to_free;
310 ga_init2(&lines_to_free, sizeof(char_u *), 50);
311
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000312 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, TRUE);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000313
314 ga_clear_strings(&lines_to_free);
315 vim_free(fga.ga_data);
316
317 if (nf != NULL && ga_grow(&objmethods, 1) == OK)
318 {
319 ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = nf;
320 ++objmethods.ga_len;
321
322 nf->uf_flags |= FC_NEW;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000323 nf->uf_ret_type = get_type_ptr(&type_list);
324 if (nf->uf_ret_type != NULL)
325 {
326 nf->uf_ret_type->tt_type = VAR_OBJECT;
327 nf->uf_ret_type->tt_member = (type_T *)cl;
328 nf->uf_ret_type->tt_argcount = 0;
329 nf->uf_ret_type->tt_args = NULL;
330 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000331 }
332 }
333
334 cl->class_obj_method_count = objmethods.ga_len;
335 cl->class_obj_methods = ALLOC_MULT(ufunc_T *, objmethods.ga_len);
336 if (cl->class_obj_methods == NULL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000337 goto cleanup;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000338 mch_memmove(cl->class_obj_methods, objmethods.ga_data,
339 sizeof(ufunc_T *) * objmethods.ga_len);
340 vim_free(objmethods.ga_data);
341
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000342 // Set the class pointer on all the object methods.
343 for (int i = 0; i < objmethods.ga_len; ++i)
344 {
345 ufunc_T *fp = cl->class_obj_methods[i];
346 fp->uf_class = cl;
347 fp->uf_flags |= FC_OBJECT; // TODO: not for class method
348 }
349
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000350 cl->class_type.tt_type = VAR_CLASS;
351 cl->class_type.tt_member = (type_T *)cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000352 cl->class_object_type.tt_type = VAR_OBJECT;
353 cl->class_object_type.tt_member = (type_T *)cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000354 cl->class_type_list = type_list;
355
356 // TODO:
357 // - Add the methods to the class
358 // - array with ufunc_T pointers
359 // - Fill hashtab with object members and methods
360 // - Generate the default new() method, if needed.
361 // Later:
362 // - class members
363 // - class methods
364
365 // Add the class to the script-local variables.
366 typval_T tv;
367 tv.v_type = VAR_CLASS;
368 tv.vval.v_class = cl;
369 set_var_const(cl->class_name, current_sctx.sc_sid,
370 NULL, &tv, FALSE, ASSIGN_DECL, 0);
371 return;
372 }
373
374cleanup:
Bram Moolenaareb533502022-12-14 15:06:11 +0000375 if (cl != NULL)
376 {
377 vim_free(cl->class_name);
378 vim_free(cl->class_obj_members);
379 vim_free(cl->class_obj_methods);
380 vim_free(cl);
381 }
382
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000383 for (int i = 0; i < objmembers.ga_len; ++i)
384 {
385 objmember_T *m = ((objmember_T *)objmembers.ga_data) + i;
386 vim_free(m->om_name);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000387 vim_free(m->om_init);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000388 }
389 ga_clear(&objmembers);
390
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000391 for (int i = 0; i < objmethods.ga_len; ++i)
392 {
393 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
394 func_clear_free(uf, FALSE);
395 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000396 ga_clear(&objmethods);
397 clear_type_list(&type_list);
398}
399
400/*
401 * Find member "name" in class "cl" and return its type.
402 * When not found t_any is returned.
403 */
404 type_T *
405class_member_type(
406 class_T *cl,
407 char_u *name,
408 char_u *name_end,
409 int *member_idx)
410{
411 *member_idx = -1; // not found (yet)
412 size_t len = name_end - name;
413
414 for (int i = 0; i < cl->class_obj_member_count; ++i)
415 {
416 objmember_T *m = cl->class_obj_members + i;
417 if (STRNCMP(m->om_name, name, len) == 0 && m->om_name[len] == NUL)
418 {
419 *member_idx = i;
420 return m->om_type;
421 }
422 }
423 return &t_any;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000424}
425
426/*
427 * Handle ":interface" up to ":endinterface".
428 */
429 void
430ex_interface(exarg_T *eap UNUSED)
431{
432 // TODO
433}
434
435/*
436 * Handle ":enum" up to ":endenum".
437 */
438 void
439ex_enum(exarg_T *eap UNUSED)
440{
441 // TODO
442}
443
444/*
445 * Handle ":type".
446 */
447 void
448ex_type(exarg_T *eap UNUSED)
449{
450 // TODO
451}
452
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000453/*
454 * Evaluate what comes after a class:
455 * - class member: SomeClass.varname
456 * - class method: SomeClass.SomeMethod()
457 * - class constructor: SomeClass.new()
458 * - object member: someObject.varname
459 * - object method: someObject.SomeMethod()
460 *
461 * "*arg" points to the '.'.
462 * "*arg" is advanced to after the member name or method call.
463 *
464 * Returns FAIL or OK.
465 */
466 int
467class_object_index(
468 char_u **arg,
469 typval_T *rettv,
470 evalarg_T *evalarg,
471 int verbose UNUSED) // give error messages
472{
473 // int evaluate = evalarg != NULL
474 // && (evalarg->eval_flags & EVAL_EVALUATE);
475
476 if (VIM_ISWHITE((*arg)[1]))
477 {
478 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
479 return FAIL;
480 }
481
482 ++*arg;
483 char_u *name = *arg;
484 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
485 if (name_end == name)
486 return FAIL;
487 size_t len = name_end - name;
488
489 class_T *cl = rettv->v_type == VAR_CLASS ? rettv->vval.v_class
490 : rettv->vval.v_object->obj_class;
491 if (*name_end == '(')
492 {
493 for (int i = 0; i < cl->class_obj_method_count; ++i)
494 {
495 ufunc_T *fp = cl->class_obj_methods[i];
Bram Moolenaar4ae00572022-12-09 22:49:23 +0000496 // Use a separate pointer to avoid that ASAN complains about
497 // uf_name[] only being 4 characters.
498 char_u *ufname = (char_u *)fp->uf_name;
499 if (STRNCMP(name, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000500 {
501 typval_T argvars[MAX_FUNC_ARGS + 1];
502 int argcount = 0;
503
504 char_u *argp = name_end;
505 int ret = get_func_arguments(&argp, evalarg, 0,
506 argvars, &argcount);
507 if (ret == FAIL)
508 return FAIL;
509
510 funcexe_T funcexe;
511 CLEAR_FIELD(funcexe);
512 funcexe.fe_evaluate = TRUE;
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000513 if (rettv->v_type == VAR_OBJECT)
514 {
515 funcexe.fe_object = rettv->vval.v_object;
516 ++funcexe.fe_object->obj_refcount;
517 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000518
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000519 // Clear the class or object after calling the function, in
520 // case the refcount is one.
521 typval_T tv_tofree = *rettv;
522 rettv->v_type = VAR_UNKNOWN;
523
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000524 // Call the user function. Result goes into rettv;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000525 int error = call_user_func_check(fp, argcount, argvars,
526 rettv, &funcexe, NULL);
527
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000528 // Clear the previous rettv and the arguments.
529 clear_tv(&tv_tofree);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000530 for (int idx = 0; idx < argcount; ++idx)
531 clear_tv(&argvars[idx]);
532
533 if (error != FCERR_NONE)
534 {
535 user_func_error(error, printable_func_name(fp),
536 funcexe.fe_found_var);
537 return FAIL;
538 }
539 *arg = argp;
540 return OK;
541 }
542 }
543
544 semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name);
545 }
546
547 else if (rettv->v_type == VAR_OBJECT)
548 {
549 for (int i = 0; i < cl->class_obj_member_count; ++i)
550 {
551 objmember_T *m = &cl->class_obj_members[i];
552 if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL)
553 {
554 // The object only contains a pointer to the class, the member
555 // values array follows right after that.
556 object_T *obj = rettv->vval.v_object;
557 typval_T *tv = (typval_T *)(obj + 1) + i;
558 copy_tv(tv, rettv);
559 object_unref(obj);
560
561 *arg = name_end;
562 return OK;
563 }
564 }
565
566 semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
567 }
568
569 // TODO: class member
570
571 return FAIL;
572}
573
574/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000575 * If "arg" points to a class or object method, return it.
576 * Otherwise return NULL.
577 */
578 ufunc_T *
579find_class_func(char_u **arg)
580{
581 char_u *name = *arg;
582 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
583 if (name_end == name || *name_end != '.')
584 return NULL;
585
586 size_t len = name_end - name;
587 typval_T tv;
588 tv.v_type = VAR_UNKNOWN;
589 if (eval_variable(name, len, 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
590 return NULL;
591 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
Bram Moolenaareb533502022-12-14 15:06:11 +0000592 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000593
594 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
595 : tv.vval.v_object->obj_class;
596 if (cl == NULL)
Bram Moolenaareb533502022-12-14 15:06:11 +0000597 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000598 char_u *fname = name_end + 1;
599 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
600 if (fname_end == fname)
Bram Moolenaareb533502022-12-14 15:06:11 +0000601 goto fail_after_eval;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000602 len = fname_end - fname;
603
604 for (int i = 0; i < cl->class_obj_method_count; ++i)
605 {
606 ufunc_T *fp = cl->class_obj_methods[i];
607 // Use a separate pointer to avoid that ASAN complains about
608 // uf_name[] only being 4 characters.
609 char_u *ufname = (char_u *)fp->uf_name;
610 if (STRNCMP(fname, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaareb533502022-12-14 15:06:11 +0000611 {
612 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000613 return fp;
Bram Moolenaareb533502022-12-14 15:06:11 +0000614 }
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000615 }
616
Bram Moolenaareb533502022-12-14 15:06:11 +0000617fail_after_eval:
618 clear_tv(&tv);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000619 return NULL;
620}
621
622/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000623 * Make a copy of an object.
624 */
625 void
626copy_object(typval_T *from, typval_T *to)
627{
628 *to = *from;
629 if (to->vval.v_object != NULL)
630 ++to->vval.v_object->obj_refcount;
631}
632
633/*
634 * Free an object.
635 */
636 static void
637object_clear(object_T *obj)
638{
639 class_T *cl = obj->obj_class;
640
641 // the member values are just after the object structure
642 typval_T *tv = (typval_T *)(obj + 1);
643 for (int i = 0; i < cl->class_obj_member_count; ++i)
644 clear_tv(tv + i);
645
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000646 // Remove from the list headed by "first_object".
647 object_cleared(obj);
648
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000649 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000650 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000651}
652
653/*
654 * Unreference an object.
655 */
656 void
657object_unref(object_T *obj)
658{
659 if (obj != NULL && --obj->obj_refcount <= 0)
660 object_clear(obj);
661}
662
663/*
664 * Make a copy of a class.
665 */
666 void
667copy_class(typval_T *from, typval_T *to)
668{
669 *to = *from;
670 if (to->vval.v_class != NULL)
671 ++to->vval.v_class->class_refcount;
672}
673
674/*
675 * Unreference a class. Free it when the reference count goes down to zero.
676 */
677 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000678class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000679{
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000680 if (cl != NULL && --cl->class_refcount <= 0)
681 {
682 vim_free(cl->class_name);
683
684 for (int i = 0; i < cl->class_obj_member_count; ++i)
685 {
686 objmember_T *m = &cl->class_obj_members[i];
687 vim_free(m->om_name);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000688 vim_free(m->om_init);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000689 }
690 vim_free(cl->class_obj_members);
691
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000692 for (int i = 0; i < cl->class_obj_method_count; ++i)
693 {
694 ufunc_T *uf = cl->class_obj_methods[i];
695 func_clear_free(uf, FALSE);
696 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000697 vim_free(cl->class_obj_methods);
698
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000699 clear_type_list(&cl->class_type_list);
700
701 vim_free(cl);
702 }
703}
704
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000705static object_T *first_object = NULL;
706
707/*
708 * Call this function when an object has been created. It will be added to the
709 * list headed by "first_object".
710 */
711 void
712object_created(object_T *obj)
713{
714 if (first_object != NULL)
715 {
716 obj->obj_next_used = first_object;
717 first_object->obj_prev_used = obj;
718 }
719 first_object = obj;
720}
721
722/*
723 * Call this function when an object has been cleared and is about to be freed.
724 * It is removed from the list headed by "first_object".
725 */
726 void
727object_cleared(object_T *obj)
728{
729 if (obj->obj_next_used != NULL)
730 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
731 if (obj->obj_prev_used != NULL)
732 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
733 else if (first_object == obj)
734 first_object = obj->obj_next_used;
735}
736
737/*
738 * Go through the list of all objects and free items without "copyID".
739 */
740 int
741object_free_nonref(int copyID)
742{
743 int did_free = FALSE;
744 object_T *next_obj;
745
746 for (object_T *obj = first_object; obj != NULL; obj = next_obj)
747 {
748 next_obj = obj->obj_next_used;
749 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
750 {
751 // Free the object and items it contains.
752 object_clear(obj);
753 did_free = TRUE;
754 }
755 }
756
757 return did_free;
758}
759
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000760
761#endif // FEAT_EVAL