blob: 395d83a1207f02a7e004e7be2a40682d367a8132 [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
150 if (ga_grow(&objmembers, 1) == FAIL)
151 break;
152 objmember_T *m = ((objmember_T *)objmembers.ga_data)
153 + objmembers.ga_len;
154 m->om_name = vim_strnsave(varname, varname_end - varname);
155 m->om_type = type;
156 ++objmembers.ga_len;
157 }
158
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000159 // constructors:
160 // def new()
161 // enddef
162 // def newOther()
163 // enddef
164 // methods:
165 // def someMethod()
166 // enddef
167 // TODO:
168 // static def someMethod()
169 // enddef
170 // def <Tval> someMethod()
171 // enddef
172 // static def <Tval> someMethod()
173 // enddef
174 else if (checkforcmd(&p, "def", 3))
175 {
176 exarg_T ea;
177 garray_T lines_to_free;
178
179 CLEAR_FIELD(ea);
180 ea.cmd = line;
181 ea.arg = p;
182 ea.cmdidx = CMD_def;
183 ea.getline = eap->getline;
184 ea.cookie = eap->cookie;
185
186 ga_init2(&lines_to_free, sizeof(char_u *), 50);
187 ufunc_T *uf = define_function(&ea, NULL, &lines_to_free, TRUE);
188 ga_clear_strings(&lines_to_free);
189
190 // TODO: how about errors?
191 if (uf != NULL && ga_grow(&objmethods, 1) == OK)
192 {
193 ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = uf;
194 ++objmethods.ga_len;
195 }
196 }
197
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000198 else
199 {
200 semsg(_(e_not_valid_command_in_class_str), line);
201 break;
202 }
203 }
204 vim_free(theline);
205
206 if (success)
207 {
208 class_T *cl = ALLOC_CLEAR_ONE(class_T);
209 if (cl == NULL)
210 goto cleanup;
211 cl->class_refcount = 1;
212 cl->class_name = vim_strnsave(arg, name_end - arg);
213
214 // Members are used by the new() function, add them here.
215 cl->class_obj_member_count = objmembers.ga_len;
Bram Moolenaar98aeb212022-12-08 22:09:14 +0000216 cl->class_obj_members = objmembers.ga_len == 0 ? NULL
217 : ALLOC_MULT(objmember_T, objmembers.ga_len);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000218 if (cl->class_name == NULL
Bram Moolenaar98aeb212022-12-08 22:09:14 +0000219 || (objmembers.ga_len > 0 && cl->class_obj_members == NULL))
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000220 {
221 vim_free(cl->class_name);
222 vim_free(cl->class_obj_members);
223 vim_free(cl);
224 goto cleanup;
225 }
226 mch_memmove(cl->class_obj_members, objmembers.ga_data,
227 sizeof(objmember_T) * objmembers.ga_len);
228 vim_free(objmembers.ga_data);
229
230 int have_new = FALSE;
231 for (int i = 0; i < objmethods.ga_len; ++i)
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000232 if (STRCMP(((ufunc_T **)objmethods.ga_data)[i]->uf_name,
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000233 "new") == 0)
234 {
235 have_new = TRUE;
236 break;
237 }
238 if (!have_new)
239 {
240 // No new() method was defined, add the default constructor.
241 garray_T fga;
242 ga_init2(&fga, 1, 1000);
243 ga_concat(&fga, (char_u *)"new(");
244 for (int i = 0; i < cl->class_obj_member_count; ++i)
245 {
246 if (i > 0)
247 ga_concat(&fga, (char_u *)", ");
248 ga_concat(&fga, (char_u *)"this.");
249 objmember_T *m = cl->class_obj_members + i;
250 ga_concat(&fga, (char_u *)m->om_name);
251 }
252 ga_concat(&fga, (char_u *)")\nenddef\n");
253 ga_append(&fga, NUL);
254
255 exarg_T fea;
256 CLEAR_FIELD(fea);
257 fea.cmdidx = CMD_def;
258 fea.cmd = fea.arg = fga.ga_data;
259
260 garray_T lines_to_free;
261 ga_init2(&lines_to_free, sizeof(char_u *), 50);
262
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000263 ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, TRUE);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000264
265 ga_clear_strings(&lines_to_free);
266 vim_free(fga.ga_data);
267
268 if (nf != NULL && ga_grow(&objmethods, 1) == OK)
269 {
270 ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = nf;
271 ++objmethods.ga_len;
272
273 nf->uf_flags |= FC_NEW;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000274 nf->uf_ret_type = get_type_ptr(&type_list);
275 if (nf->uf_ret_type != NULL)
276 {
277 nf->uf_ret_type->tt_type = VAR_OBJECT;
278 nf->uf_ret_type->tt_member = (type_T *)cl;
279 nf->uf_ret_type->tt_argcount = 0;
280 nf->uf_ret_type->tt_args = NULL;
281 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000282 }
283 }
284
285 cl->class_obj_method_count = objmethods.ga_len;
286 cl->class_obj_methods = ALLOC_MULT(ufunc_T *, objmethods.ga_len);
287 if (cl->class_obj_methods == NULL)
288 {
289 vim_free(cl->class_name);
290 vim_free(cl->class_obj_members);
291 vim_free(cl->class_obj_methods);
292 vim_free(cl);
293 goto cleanup;
294 }
295 mch_memmove(cl->class_obj_methods, objmethods.ga_data,
296 sizeof(ufunc_T *) * objmethods.ga_len);
297 vim_free(objmethods.ga_data);
298
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000299 // Set the class pointer on all the object methods.
300 for (int i = 0; i < objmethods.ga_len; ++i)
301 {
302 ufunc_T *fp = cl->class_obj_methods[i];
303 fp->uf_class = cl;
304 fp->uf_flags |= FC_OBJECT; // TODO: not for class method
305 }
306
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000307 cl->class_type.tt_type = VAR_CLASS;
308 cl->class_type.tt_member = (type_T *)cl;
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000309 cl->class_object_type.tt_type = VAR_OBJECT;
310 cl->class_object_type.tt_member = (type_T *)cl;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000311 cl->class_type_list = type_list;
312
313 // TODO:
314 // - Add the methods to the class
315 // - array with ufunc_T pointers
316 // - Fill hashtab with object members and methods
317 // - Generate the default new() method, if needed.
318 // Later:
319 // - class members
320 // - class methods
321
322 // Add the class to the script-local variables.
323 typval_T tv;
324 tv.v_type = VAR_CLASS;
325 tv.vval.v_class = cl;
326 set_var_const(cl->class_name, current_sctx.sc_sid,
327 NULL, &tv, FALSE, ASSIGN_DECL, 0);
328 return;
329 }
330
331cleanup:
332 for (int i = 0; i < objmembers.ga_len; ++i)
333 {
334 objmember_T *m = ((objmember_T *)objmembers.ga_data) + i;
335 vim_free(m->om_name);
336 }
337 ga_clear(&objmembers);
338
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000339 for (int i = 0; i < objmethods.ga_len; ++i)
340 {
341 ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
342 func_clear_free(uf, FALSE);
343 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000344 ga_clear(&objmethods);
345 clear_type_list(&type_list);
346}
347
348/*
349 * Find member "name" in class "cl" and return its type.
350 * When not found t_any is returned.
351 */
352 type_T *
353class_member_type(
354 class_T *cl,
355 char_u *name,
356 char_u *name_end,
357 int *member_idx)
358{
359 *member_idx = -1; // not found (yet)
360 size_t len = name_end - name;
361
362 for (int i = 0; i < cl->class_obj_member_count; ++i)
363 {
364 objmember_T *m = cl->class_obj_members + i;
365 if (STRNCMP(m->om_name, name, len) == 0 && m->om_name[len] == NUL)
366 {
367 *member_idx = i;
368 return m->om_type;
369 }
370 }
371 return &t_any;
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000372}
373
374/*
375 * Handle ":interface" up to ":endinterface".
376 */
377 void
378ex_interface(exarg_T *eap UNUSED)
379{
380 // TODO
381}
382
383/*
384 * Handle ":enum" up to ":endenum".
385 */
386 void
387ex_enum(exarg_T *eap UNUSED)
388{
389 // TODO
390}
391
392/*
393 * Handle ":type".
394 */
395 void
396ex_type(exarg_T *eap UNUSED)
397{
398 // TODO
399}
400
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000401/*
402 * Evaluate what comes after a class:
403 * - class member: SomeClass.varname
404 * - class method: SomeClass.SomeMethod()
405 * - class constructor: SomeClass.new()
406 * - object member: someObject.varname
407 * - object method: someObject.SomeMethod()
408 *
409 * "*arg" points to the '.'.
410 * "*arg" is advanced to after the member name or method call.
411 *
412 * Returns FAIL or OK.
413 */
414 int
415class_object_index(
416 char_u **arg,
417 typval_T *rettv,
418 evalarg_T *evalarg,
419 int verbose UNUSED) // give error messages
420{
421 // int evaluate = evalarg != NULL
422 // && (evalarg->eval_flags & EVAL_EVALUATE);
423
424 if (VIM_ISWHITE((*arg)[1]))
425 {
426 semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
427 return FAIL;
428 }
429
430 ++*arg;
431 char_u *name = *arg;
432 char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
433 if (name_end == name)
434 return FAIL;
435 size_t len = name_end - name;
436
437 class_T *cl = rettv->v_type == VAR_CLASS ? rettv->vval.v_class
438 : rettv->vval.v_object->obj_class;
439 if (*name_end == '(')
440 {
441 for (int i = 0; i < cl->class_obj_method_count; ++i)
442 {
443 ufunc_T *fp = cl->class_obj_methods[i];
444 if (STRNCMP(name, fp->uf_name, len) == 0 && fp->uf_name[len] == NUL)
445 {
446 typval_T argvars[MAX_FUNC_ARGS + 1];
447 int argcount = 0;
448
449 char_u *argp = name_end;
450 int ret = get_func_arguments(&argp, evalarg, 0,
451 argvars, &argcount);
452 if (ret == FAIL)
453 return FAIL;
454
455 funcexe_T funcexe;
456 CLEAR_FIELD(funcexe);
457 funcexe.fe_evaluate = TRUE;
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000458 if (rettv->v_type == VAR_OBJECT)
459 {
460 funcexe.fe_object = rettv->vval.v_object;
461 ++funcexe.fe_object->obj_refcount;
462 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000463
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000464 // Clear the class or object after calling the function, in
465 // case the refcount is one.
466 typval_T tv_tofree = *rettv;
467 rettv->v_type = VAR_UNKNOWN;
468
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000469 // Call the user function. Result goes into rettv;
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000470 int error = call_user_func_check(fp, argcount, argvars,
471 rettv, &funcexe, NULL);
472
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000473 // Clear the previous rettv and the arguments.
474 clear_tv(&tv_tofree);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000475 for (int idx = 0; idx < argcount; ++idx)
476 clear_tv(&argvars[idx]);
477
478 if (error != FCERR_NONE)
479 {
480 user_func_error(error, printable_func_name(fp),
481 funcexe.fe_found_var);
482 return FAIL;
483 }
484 *arg = argp;
485 return OK;
486 }
487 }
488
489 semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name);
490 }
491
492 else if (rettv->v_type == VAR_OBJECT)
493 {
494 for (int i = 0; i < cl->class_obj_member_count; ++i)
495 {
496 objmember_T *m = &cl->class_obj_members[i];
497 if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL)
498 {
499 // The object only contains a pointer to the class, the member
500 // values array follows right after that.
501 object_T *obj = rettv->vval.v_object;
502 typval_T *tv = (typval_T *)(obj + 1) + i;
503 copy_tv(tv, rettv);
504 object_unref(obj);
505
506 *arg = name_end;
507 return OK;
508 }
509 }
510
511 semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
512 }
513
514 // TODO: class member
515
516 return FAIL;
517}
518
519/*
520 * Make a copy of an object.
521 */
522 void
523copy_object(typval_T *from, typval_T *to)
524{
525 *to = *from;
526 if (to->vval.v_object != NULL)
527 ++to->vval.v_object->obj_refcount;
528}
529
530/*
531 * Free an object.
532 */
533 static void
534object_clear(object_T *obj)
535{
536 class_T *cl = obj->obj_class;
537
538 // the member values are just after the object structure
539 typval_T *tv = (typval_T *)(obj + 1);
540 for (int i = 0; i < cl->class_obj_member_count; ++i)
541 clear_tv(tv + i);
542
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000543 // Remove from the list headed by "first_object".
544 object_cleared(obj);
545
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000546 vim_free(obj);
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000547 class_unref(cl);
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000548}
549
550/*
551 * Unreference an object.
552 */
553 void
554object_unref(object_T *obj)
555{
556 if (obj != NULL && --obj->obj_refcount <= 0)
557 object_clear(obj);
558}
559
560/*
561 * Make a copy of a class.
562 */
563 void
564copy_class(typval_T *from, typval_T *to)
565{
566 *to = *from;
567 if (to->vval.v_class != NULL)
568 ++to->vval.v_class->class_refcount;
569}
570
571/*
572 * Unreference a class. Free it when the reference count goes down to zero.
573 */
574 void
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000575class_unref(class_T *cl)
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000576{
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000577 if (cl != NULL && --cl->class_refcount <= 0)
578 {
579 vim_free(cl->class_name);
580
581 for (int i = 0; i < cl->class_obj_member_count; ++i)
582 {
583 objmember_T *m = &cl->class_obj_members[i];
584 vim_free(m->om_name);
585 }
586 vim_free(cl->class_obj_members);
587
Bram Moolenaarffdaca92022-12-09 21:41:48 +0000588 for (int i = 0; i < cl->class_obj_method_count; ++i)
589 {
590 ufunc_T *uf = cl->class_obj_methods[i];
591 func_clear_free(uf, FALSE);
592 }
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000593 vim_free(cl->class_obj_methods);
594
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000595 clear_type_list(&cl->class_type_list);
596
597 vim_free(cl);
598 }
599}
600
Bram Moolenaard28d7b92022-12-08 20:42:00 +0000601static object_T *first_object = NULL;
602
603/*
604 * Call this function when an object has been created. It will be added to the
605 * list headed by "first_object".
606 */
607 void
608object_created(object_T *obj)
609{
610 if (first_object != NULL)
611 {
612 obj->obj_next_used = first_object;
613 first_object->obj_prev_used = obj;
614 }
615 first_object = obj;
616}
617
618/*
619 * Call this function when an object has been cleared and is about to be freed.
620 * It is removed from the list headed by "first_object".
621 */
622 void
623object_cleared(object_T *obj)
624{
625 if (obj->obj_next_used != NULL)
626 obj->obj_next_used->obj_prev_used = obj->obj_prev_used;
627 if (obj->obj_prev_used != NULL)
628 obj->obj_prev_used->obj_next_used = obj->obj_next_used;
629 else if (first_object == obj)
630 first_object = obj->obj_next_used;
631}
632
633/*
634 * Go through the list of all objects and free items without "copyID".
635 */
636 int
637object_free_nonref(int copyID)
638{
639 int did_free = FALSE;
640 object_T *next_obj;
641
642 for (object_T *obj = first_object; obj != NULL; obj = next_obj)
643 {
644 next_obj = obj->obj_next_used;
645 if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
646 {
647 // Free the object and items it contains.
648 object_clear(obj);
649 did_free = TRUE;
650 }
651 }
652
653 return did_free;
654}
655
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000656
657#endif // FEAT_EVAL