blob: c8cea3c1a5b0138445054b571ba9430eee8a8b56 [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);
128 // TODO: accept initialization and figure out type from it
129 if (*colon != ':')
130 {
131 emsg(_(e_type_or_initialization_required));
132 break;
133 }
134 if (VIM_ISWHITE(*varname_end))
135 {
136 semsg(_(e_no_white_space_allowed_before_colon_str), varname);
137 break;
138 }
139 if (!VIM_ISWHITE(colon[1]))
140 {
141 semsg(_(e_white_space_required_after_str_str), ":", varname);
142 break;
143 }
144
145 char_u *type_arg = skipwhite(colon + 1);
146 type_T *type = parse_type(&type_arg, &type_list, TRUE);
147 if (type == NULL)
148 break;
149
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000150 char_u *expr_start = skipwhite(type_arg);
151 if (*expr_start == '=' && (!VIM_ISWHITE(expr_start[-1])
152 || !VIM_ISWHITE(expr_start[1])))
153 {
154 semsg(_(e_white_space_required_before_and_after_str_at_str),
155 "=", type_arg);
156 break;
157 }
158 expr_start = skipwhite(expr_start + 1);
159
160 char_u *expr_end = expr_start;
161 evalarg_T evalarg;
162 init_evalarg(&evalarg);
163 skip_expr(&expr_end, &evalarg);
164 clear_evalarg(&evalarg, NULL);
165
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000166 if (ga_grow(&objmembers, 1) == FAIL)
167 break;
168 objmember_T *m = ((objmember_T *)objmembers.ga_data)
169 + objmembers.ga_len;
170 m->om_name = vim_strnsave(varname, varname_end - varname);
171 m->om_type = type;
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000172 if (expr_end > expr_start)
173 m->om_init = vim_strnsave(expr_start, expr_end - expr_start);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000174 ++objmembers.ga_len;
175 }
176
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000177 // constructors:
178 // def new()
179 // enddef
180 // def newOther()
181 // enddef
182 // methods:
183 // def someMethod()
184 // enddef
185 // TODO:
186 // static def someMethod()
187 // enddef
188 // def <Tval> someMethod()
189 // enddef
190 // static def <Tval> someMethod()
191 // enddef
192 else if (checkforcmd(&p, "def", 3))
193 {
194 exarg_T ea;
195 garray_T lines_to_free;
196
197 CLEAR_FIELD(ea);
198 ea.cmd = line;
199 ea.arg = p;
200 ea.cmdidx = CMD_def;
201 ea.getline = eap->getline;
202 ea.cookie = eap->cookie;
203
204 ga_init2(&lines_to_free, sizeof(char_u *), 50);
205 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free, TRUE);
206 ga_clear_strings(&lines_to_free);
207
208 // TODO: how about errors?
209 if (uf != NULL && ga_grow(&objmethods, 1) == OK)
210 {
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000211 if (STRNCMP(uf->uf_name, "new", 3) == 0)
212 uf->uf_flags |= FC_NEW;
213
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000214 ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = uf;
215 ++objmethods.ga_len;
216 }
217 }
218
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000219 else
220 {
221 semsg(_(e_not_valid_command_in_class_str), line);
222 break;
223 }
224 }
225 vim_free(theline);
226
227 if (success)
228 {
229 class_T *cl = ALLOC_CLEAR_ONE(class_T);
230 if (cl == NULL)
231 goto cleanup;
232 cl->class_refcount = 1;
233 cl->class_name = vim_strnsave(arg, name_end - arg);
234
235 // Members are used by the new() function, add them here.
236 cl->class_obj_member_count = objmembers.ga_len;
Bram Moolenaar98aeb212022-12-08 22:09:14 +0000237 cl->class_obj_members = objmembers.ga_len == 0 ? NULL
238 : ALLOC_MULT(objmember_T, objmembers.ga_len);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000239 if (cl->class_name == NULL
Bram Moolenaar98aeb212022-12-08 22:09:14 +0000240 || (objmembers.ga_len > 0 && cl->class_obj_members == NULL))
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000241 {
242 vim_free(cl->class_name);
243 vim_free(cl->class_obj_members);
244 vim_free(cl);
245 goto cleanup;
246 }
247 mch_memmove(cl->class_obj_members, objmembers.ga_data,
248 sizeof(objmember_T) * objmembers.ga_len);
249 vim_free(objmembers.ga_data);
250
251 int have_new = FALSE;
252 for (int i = 0; i < objmethods.ga_len; ++i)
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000253 if (STRCMP(((ufunc_T **)objmethods.ga_data)[i]->uf_name,
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000254 "new") == 0)
255 {
256 have_new = TRUE;
257 break;
258 }
259 if (!have_new)
260 {
261 // No new() method was defined, add the default constructor.
262 garray_T fga;
263 ga_init2(&fga, 1, 1000);
264 ga_concat(&fga, (char_u *)"new(");
265 for (int i = 0; i < cl->class_obj_member_count; ++i)
266 {
267 if (i > 0)
268 ga_concat(&fga, (char_u *)", ");
269 ga_concat(&fga, (char_u *)"this.");
270 objmember_T *m = cl->class_obj_members + i;
271 ga_concat(&fga, (char_u *)m->om_name);
272 }
273 ga_concat(&fga, (char_u *)")\nenddef\n");
274 ga_append(&fga, NUL);
275
276 exarg_T fea;
277 CLEAR_FIELD(fea);
278 fea.cmdidx = CMD_def;
279 fea.cmd = fea.arg = fga.ga_data;
280
281 garray_T lines_to_free;
282 ga_init2(&lines_to_free, sizeof(char_u *), 50);
283
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000284 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, TRUE);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000285
286 ga_clear_strings(&lines_to_free);
287 vim_free(fga.ga_data);
288
289 if (nf != NULL && ga_grow(&objmethods, 1) == OK)
290 {
291 ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = nf;
292 ++objmethods.ga_len;
293
294 nf->uf_flags |= FC_NEW;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000295 nf->uf_ret_type = get_type_ptr(&type_list);
296 if (nf->uf_ret_type != NULL)
297 {
298 nf->uf_ret_type->tt_type = VAR_OBJECT;
299 nf->uf_ret_type->tt_member = (type_T *)cl;
300 nf->uf_ret_type->tt_argcount = 0;
301 nf->uf_ret_type->tt_args = NULL;
302 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000303 }
304 }
305
306 cl->class_obj_method_count = objmethods.ga_len;
307 cl->class_obj_methods = ALLOC_MULT(ufunc_T *, objmethods.ga_len);
308 if (cl->class_obj_methods == NULL)
309 {
310 vim_free(cl->class_name);
311 vim_free(cl->class_obj_members);
312 vim_free(cl->class_obj_methods);
313 vim_free(cl);
314 goto cleanup;
315 }
316 mch_memmove(cl->class_obj_methods, objmethods.ga_data,
317 sizeof(ufunc_T *) * objmethods.ga_len);
318 vim_free(objmethods.ga_data);
319
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000320 // Set the class pointer on all the object methods.
321 for (int i = 0; i < objmethods.ga_len; ++i)
322 {
323 ufunc_T *fp = cl->class_obj_methods[i];
324 fp->uf_class = cl;
325 fp->uf_flags |= FC_OBJECT; // TODO: not for class method
326 }
327
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000328 cl->class_type.tt_type = VAR_CLASS;
329 cl->class_type.tt_member = (type_T *)cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000330 cl->class_object_type.tt_type = VAR_OBJECT;
331 cl->class_object_type.tt_member = (type_T *)cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000332 cl->class_type_list = type_list;
333
334 // TODO:
335 // - Add the methods to the class
336 // - array with ufunc_T pointers
337 // - Fill hashtab with object members and methods
338 // - Generate the default new() method, if needed.
339 // Later:
340 // - class members
341 // - class methods
342
343 // Add the class to the script-local variables.
344 typval_T tv;
345 tv.v_type = VAR_CLASS;
346 tv.vval.v_class = cl;
347 set_var_const(cl->class_name, current_sctx.sc_sid,
348 NULL, &tv, FALSE, ASSIGN_DECL, 0);
349 return;
350 }
351
352cleanup:
353 for (int i = 0; i < objmembers.ga_len; ++i)
354 {
355 objmember_T *m = ((objmember_T *)objmembers.ga_data) + i;
356 vim_free(m->om_name);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000357 vim_free(m->om_init);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000358 }
359 ga_clear(&objmembers);
360
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000361 for (int i = 0; i < objmethods.ga_len; ++i)
362 {
363 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
364 func_clear_free(uf, FALSE);
365 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000366 ga_clear(&objmethods);
367 clear_type_list(&type_list);
368}
369
370/*
371 * Find member "name" in class "cl" and return its type.
372 * When not found t_any is returned.
373 */
374 type_T *
375class_member_type(
376 class_T *cl,
377 char_u *name,
378 char_u *name_end,
379 int *member_idx)
380{
381 *member_idx = -1; // not found (yet)
382 size_t len = name_end - name;
383
384 for (int i = 0; i < cl->class_obj_member_count; ++i)
385 {
386 objmember_T *m = cl->class_obj_members + i;
387 if (STRNCMP(m->om_name, name, len) == 0 && m->om_name[len] == NUL)
388 {
389 *member_idx = i;
390 return m->om_type;
391 }
392 }
393 return &t_any;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000394}
395
396/*
397 * Handle ":interface" up to ":endinterface".
398 */
399 void
400ex_interface(exarg_T *eap UNUSED)
401{
402 // TODO
403}
404
405/*
406 * Handle ":enum" up to ":endenum".
407 */
408 void
409ex_enum(exarg_T *eap UNUSED)
410{
411 // TODO
412}
413
414/*
415 * Handle ":type".
416 */
417 void
418ex_type(exarg_T *eap UNUSED)
419{
420 // TODO
421}
422
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000423/*
424 * Evaluate what comes after a class:
425 * - class member: SomeClass.varname
426 * - class method: SomeClass.SomeMethod()
427 * - class constructor: SomeClass.new()
428 * - object member: someObject.varname
429 * - object method: someObject.SomeMethod()
430 *
431 * "*arg" points to the '.'.
432 * "*arg" is advanced to after the member name or method call.
433 *
434 * Returns FAIL or OK.
435 */
436 int
437class_object_index(
438 char_u **arg,
439 typval_T *rettv,
440 evalarg_T *evalarg,
441 int verbose UNUSED) // give error messages
442{
443 // int evaluate = evalarg != NULL
444 // && (evalarg->eval_flags & EVAL_EVALUATE);
445
446 if (VIM_ISWHITE((*arg)[1]))
447 {
448 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
449 return FAIL;
450 }
451
452 ++*arg;
453 char_u *name = *arg;
454 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
455 if (name_end == name)
456 return FAIL;
457 size_t len = name_end - name;
458
459 class_T *cl = rettv->v_type == VAR_CLASS ? rettv->vval.v_class
460 : rettv->vval.v_object->obj_class;
461 if (*name_end == '(')
462 {
463 for (int i = 0; i < cl->class_obj_method_count; ++i)
464 {
465 ufunc_T *fp = cl->class_obj_methods[i];
Bram Moolenaar4ae00572022-12-09 22:49:23 +0000466 // Use a separate pointer to avoid that ASAN complains about
467 // uf_name[] only being 4 characters.
468 char_u *ufname = (char_u *)fp->uf_name;
469 if (STRNCMP(name, ufname, len) == 0 && ufname[len] == NUL)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000470 {
471 typval_T argvars[MAX_FUNC_ARGS + 1];
472 int argcount = 0;
473
474 char_u *argp = name_end;
475 int ret = get_func_arguments(&argp, evalarg, 0,
476 argvars, &argcount);
477 if (ret == FAIL)
478 return FAIL;
479
480 funcexe_T funcexe;
481 CLEAR_FIELD(funcexe);
482 funcexe.fe_evaluate = TRUE;
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000483 if (rettv->v_type == VAR_OBJECT)
484 {
485 funcexe.fe_object = rettv->vval.v_object;
486 ++funcexe.fe_object->obj_refcount;
487 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000488
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000489 // Clear the class or object after calling the function, in
490 // case the refcount is one.
491 typval_T tv_tofree = *rettv;
492 rettv->v_type = VAR_UNKNOWN;
493
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000494 // Call the user function. Result goes into rettv;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000495 int error = call_user_func_check(fp, argcount, argvars,
496 rettv, &funcexe, NULL);
497
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000498 // Clear the previous rettv and the arguments.
499 clear_tv(&tv_tofree);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000500 for (int idx = 0; idx < argcount; ++idx)
501 clear_tv(&argvars[idx]);
502
503 if (error != FCERR_NONE)
504 {
505 user_func_error(error, printable_func_name(fp),
506 funcexe.fe_found_var);
507 return FAIL;
508 }
509 *arg = argp;
510 return OK;
511 }
512 }
513
514 semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name);
515 }
516
517 else if (rettv->v_type == VAR_OBJECT)
518 {
519 for (int i = 0; i < cl->class_obj_member_count; ++i)
520 {
521 objmember_T *m = &cl->class_obj_members[i];
522 if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL)
523 {
524 // The object only contains a pointer to the class, the member
525 // values array follows right after that.
526 object_T *obj = rettv->vval.v_object;
527 typval_T *tv = (typval_T *)(obj + 1) + i;
528 copy_tv(tv, rettv);
529 object_unref(obj);
530
531 *arg = name_end;
532 return OK;
533 }
534 }
535
536 semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
537 }
538
539 // TODO: class member
540
541 return FAIL;
542}
543
544/*
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000545 * If "arg" points to a class or object method, return it.
546 * Otherwise return NULL.
547 */
548 ufunc_T *
549find_class_func(char_u **arg)
550{
551 char_u *name = *arg;
552 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
553 if (name_end == name || *name_end != '.')
554 return NULL;
555
556 size_t len = name_end - name;
557 typval_T tv;
558 tv.v_type = VAR_UNKNOWN;
559 if (eval_variable(name, len, 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
560 return NULL;
561 if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
562 {
563 clear_tv(&tv);
564 return NULL;
565 }
566
567 class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
568 : tv.vval.v_object->obj_class;
569 if (cl == NULL)
570 return NULL;
571 char_u *fname = name_end + 1;
572 char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
573 if (fname_end == fname)
574 return NULL;
575 len = fname_end - fname;
576
577 for (int i = 0; i < cl->class_obj_method_count; ++i)
578 {
579 ufunc_T *fp = cl->class_obj_methods[i];
580 // Use a separate pointer to avoid that ASAN complains about
581 // uf_name[] only being 4 characters.
582 char_u *ufname = (char_u *)fp->uf_name;
583 if (STRNCMP(fname, ufname, len) == 0 && ufname[len] == NUL)
584 return fp;
585 }
586
587 return NULL;
588}
589
590/*
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000591 * Make a copy of an object.
592 */
593 void
594copy_object(typval_T *from, typval_T *to)
595{
596 *to = *from;
597 if (to->vval.v_object != NULL)
598 ++to->vval.v_object->obj_refcount;
599}
600
601/*
602 * Free an object.
603 */
604 static void
605object_clear(object_T *obj)
606{
607 class_T *cl = obj->obj_class;
608
609 // the member values are just after the object structure
610 typval_T *tv = (typval_T *)(obj + 1);
611 for (int i = 0; i < cl->class_obj_member_count; ++i)
612 clear_tv(tv + i);
613
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000614 // Remove from the list headed by "first_object".
615 object_cleared(obj);
616
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000617 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000618 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000619}
620
621/*
622 * Unreference an object.
623 */
624 void
625object_unref(object_T *obj)
626{
627 if (obj != NULL && --obj->obj_refcount <= 0)
628 object_clear(obj);
629}
630
631/*
632 * Make a copy of a class.
633 */
634 void
635copy_class(typval_T *from, typval_T *to)
636{
637 *to = *from;
638 if (to->vval.v_class != NULL)
639 ++to->vval.v_class->class_refcount;
640}
641
642/*
643 * Unreference a class. Free it when the reference count goes down to zero.
644 */
645 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000646class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000647{
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000648 if (cl != NULL && --cl->class_refcount <= 0)
649 {
650 vim_free(cl->class_name);
651
652 for (int i = 0; i < cl->class_obj_member_count; ++i)
653 {
654 objmember_T *m = &cl->class_obj_members[i];
655 vim_free(m->om_name);
Bram Moolenaar7ce7daf2022-12-10 18:42:12 +0000656 vim_free(m->om_init);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000657 }
658 vim_free(cl->class_obj_members);
659
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000660 for (int i = 0; i < cl->class_obj_method_count; ++i)
661 {
662 ufunc_T *uf = cl->class_obj_methods[i];
663 func_clear_free(uf, FALSE);
664 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000665 vim_free(cl->class_obj_methods);
666
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000667 clear_type_list(&cl->class_type_list);
668
669 vim_free(cl);
670 }
671}
672
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000673static object_T *first_object = NULL;
674
675/*
676 * Call this function when an object has been created. It will be added to the
677 * list headed by "first_object".
678 */
679 void
680object_created(object_T *obj)
681{
682 if (first_object != NULL)
683 {
684 obj->obj_next_used = first_object;
685 first_object->obj_prev_used = obj;
686 }
687 first_object = obj;
688}
689
690/*
691 * Call this function when an object has been cleared and is about to be freed.
692 * It is removed from the list headed by "first_object".
693 */
694 void
695object_cleared(object_T *obj)
696{
697 if (obj->obj_next_used != NULL)
698 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
699 if (obj->obj_prev_used != NULL)
700 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
701 else if (first_object == obj)
702 first_object = obj->obj_next_used;
703}
704
705/*
706 * Go through the list of all objects and free items without "copyID".
707 */
708 int
709object_free_nonref(int copyID)
710{
711 int did_free = FALSE;
712 object_T *next_obj;
713
714 for (object_T *obj = first_object; obj != NULL; obj = next_obj)
715 {
716 next_obj = obj->obj_next_used;
717 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
718 {
719 // Free the object and items it contains.
720 object_clear(obj);
721 did_free = TRUE;
722 }
723 }
724
725 return did_free;
726}
727
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000728
729#endif // FEAT_EVAL