comparison src/vim9class.c @ 32854:5fd9fe58c791 v9.0.1737

patch 9.0.1737: Calling a base class method through an extended class fails Commit: https://github.com/vim/vim/commit/b102728c204430b16a5d9e6720c882e12a687570 Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Sat Aug 19 11:26:42 2023 +0200 patch 9.0.1737: Calling a base class method through an extended class fails Problem: Calling a base class method through an extended class fails Solution: Create lookup table for member index in the interface to to the member class implementing the interface Create additional tests for Vim9 classes. Fix unconvered memory leaks and crashes found by the new tests. closes: #12848 closes: #12089 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>author
author Christian Brabandt <cb@256bit.org>
date Sat, 19 Aug 2023 11:45:03 +0200
parents 57282f1d9e0f
children a39314fa9495
comparison
equal deleted inserted replaced
32853:1061521240a3 32854:5fd9fe58c791
226 cl->class_name, itf->class_name); 226 cl->class_name, itf->class_name);
227 return 0; 227 return 0;
228 } 228 }
229 int *table = (int *)(i2c + 1); 229 int *table = (int *)(i2c + 1);
230 return table[idx]; 230 return table[idx];
231 }
232
233 /*
234 * Update the interface class lookup table for the member index on the
235 * interface to the member index in the class implementing the interface.
236 * And a lookup table for the object method index on the interface
237 * to the object method index in the class implementing the interface.
238 * This is also used for updating the lookup table for the extended class
239 * hierarchy.
240 */
241 static int
242 update_member_method_lookup_table(
243 class_T *ifcl,
244 class_T *cl,
245 garray_T *objmethods,
246 int pobj_method_offset,
247 int is_interface)
248 {
249 if (ifcl == NULL)
250 return OK;
251
252 // Table for members.
253 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
254 + ifcl->class_obj_member_count * sizeof(int));
255 if (if2cl == NULL)
256 return FAIL;
257 if2cl->i2c_next = ifcl->class_itf2class;
258 ifcl->class_itf2class = if2cl;
259 if2cl->i2c_class = cl;
260 if2cl->i2c_is_method = FALSE;
261
262 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
263 for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
264 {
265 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
266 cl->class_obj_members[cl_i].ocm_name) == 0)
267 {
268 int *table = (int *)(if2cl + 1);
269 table[if_i] = cl_i;
270 break;
271 }
272 }
273
274 // Table for methods.
275 if2cl = alloc_clear(sizeof(itf2class_T)
276 + ifcl->class_obj_method_count * sizeof(int));
277 if (if2cl == NULL)
278 return FAIL;
279 if2cl->i2c_next = ifcl->class_itf2class;
280 ifcl->class_itf2class = if2cl;
281 if2cl->i2c_class = cl;
282 if2cl->i2c_is_method = TRUE;
283
284 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
285 {
286 int done = FALSE;
287 for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
288 {
289 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
290 ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name)
291 == 0)
292 {
293 int *table = (int *)(if2cl + 1);
294 table[if_i] = cl_i;
295 done = TRUE;
296 break;
297 }
298 }
299
300 // extended class object method is not overridden by the child class.
301 // Keep the method declared in one of the parent classes in the
302 // lineage.
303 if (!done && !is_interface)
304 {
305 // If "ifcl" is not the immediate parent of "cl", then search in
306 // the intermediate parent classes.
307 if (cl->class_extends != ifcl)
308 {
309 class_T *parent = cl->class_extends;
310 int method_offset = objmethods->ga_len;
311
312 while (!done && parent != NULL && parent != ifcl)
313 {
314
315 for (int cl_i = 0;
316 cl_i < parent->class_obj_method_count_child; ++cl_i)
317 {
318 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
319 parent->class_obj_methods[cl_i]->uf_name)
320 == 0)
321 {
322 int *table = (int *)(if2cl + 1);
323 table[if_i] = method_offset + cl_i;
324 done = TRUE;
325 break;
326 }
327 }
328 method_offset += parent->class_obj_method_count_child;
329 parent = parent->class_extends;
330 }
331 }
332
333 if (!done)
334 {
335 int *table = (int *)(if2cl + 1);
336 table[if_i] = pobj_method_offset + if_i;
337 }
338 }
339 }
340
341 return OK;
231 } 342 }
232 343
233 /* 344 /*
234 * Handle ":class" and ":abstract class" up to ":endclass". 345 * Handle ":class" and ":abstract class" up to ":endclass".
235 * Handle ":interface" up to ":endinterface". 346 * Handle ":interface" up to ":endinterface".
532 int is_new = STRNCMP(name, "new", 3) == 0; 643 int is_new = STRNCMP(name, "new", 3) == 0;
533 if (is_new && is_abstract) 644 if (is_new && is_abstract)
534 { 645 {
535 emsg(_(e_cannot_define_new_function_in_abstract_class)); 646 emsg(_(e_cannot_define_new_function_in_abstract_class));
536 success = FALSE; 647 success = FALSE;
648 func_clear_free(uf, FALSE);
537 break; 649 break;
538 } 650 }
539 garray_T *fgap = has_static || is_new 651 garray_T *fgap = has_static || is_new
540 ? &classfunctions : &objmethods; 652 ? &classfunctions : &objmethods;
541 // Check the name isn't used already. 653 // Check the name isn't used already.
652 || tv.vval.v_class == NULL 764 || tv.vval.v_class == NULL
653 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0) 765 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
654 { 766 {
655 semsg(_(e_not_valid_interface_str), impl); 767 semsg(_(e_not_valid_interface_str), impl);
656 success = FALSE; 768 success = FALSE;
769 clear_tv(&tv);
770 break;
657 } 771 }
658 772
659 class_T *ifcl = tv.vval.v_class; 773 class_T *ifcl = tv.vval.v_class;
660 intf_classes[i] = ifcl; 774 intf_classes[i] = ifcl;
661 ++ifcl->class_refcount; 775 ++ifcl->class_refcount;
837 intf_classes = NULL; 951 intf_classes = NULL;
838 } 952 }
839 953
840 if (cl->class_interface_count > 0 || extends_cl != NULL) 954 if (cl->class_interface_count > 0 || extends_cl != NULL)
841 { 955 {
842 // For each interface add a lookuptable for the member index on the 956 // For each interface add a lookup table for the member index on
843 // interface to the member index in this class. 957 // the interface to the member index in this class.
844 // And a lookuptable for the object method index on the interface 958 // And a lookup table for the object method index on the interface
845 // to the object method index in this class. 959 // to the object method index in this class.
846 // Also do this for the extended class, if any. 960 for (int i = 0; i < cl->class_interface_count; ++i)
847 for (int i = 0; i <= cl->class_interface_count; ++i) 961 {
848 { 962 class_T *ifcl = cl->class_interfaces_cl[i];
849 class_T *ifcl = i < cl->class_interface_count 963
850 ? cl->class_interfaces_cl[i] 964 if (update_member_method_lookup_table(ifcl, cl, &objmethods,
851 : extends_cl; 965 0, TRUE) == FAIL)
852 if (ifcl == NULL)
853 continue;
854
855 // Table for members.
856 itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
857 + ifcl->class_obj_member_count * sizeof(int));
858 if (if2cl == NULL)
859 goto cleanup; 966 goto cleanup;
860 if2cl->i2c_next = ifcl->class_itf2class; 967 }
861 ifcl->class_itf2class = if2cl; 968
862 if2cl->i2c_class = cl; 969 // Update the lookup table for the extended class, if nay
863 if2cl->i2c_is_method = FALSE; 970 if (extends_cl != NULL)
864 971 {
865 for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i) 972 class_T *pclass = extends_cl;
866 for (int cl_i = 0; cl_i < cl->class_obj_member_count; 973 int pobj_method_offset = objmethods.ga_len;
867 ++cl_i) 974
868 { 975 // Update the entire lineage of extended classes.
869 if (STRCMP(ifcl->class_obj_members[if_i].ocm_name, 976 while (pclass != NULL)
870 cl->class_obj_members[cl_i].ocm_name) == 0)
871 {
872 int *table = (int *)(if2cl + 1);
873 table[if_i] = cl_i;
874 break;
875 }
876 }
877
878 // Table for methods.
879 if2cl = alloc_clear(sizeof(itf2class_T)
880 + ifcl->class_obj_method_count * sizeof(int));
881 if (if2cl == NULL)
882 goto cleanup;
883 if2cl->i2c_next = ifcl->class_itf2class;
884 ifcl->class_itf2class = if2cl;
885 if2cl->i2c_class = cl;
886 if2cl->i2c_is_method = TRUE;
887
888 for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
889 { 977 {
890 int done = FALSE; 978 if (update_member_method_lookup_table(pclass, cl,
891 for (int cl_i = 0; cl_i < objmethods.ga_len; ++cl_i) 979 &objmethods, pobj_method_offset, FALSE) ==
892 { 980 FAIL)
893 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name, 981 goto cleanup;
894 ((ufunc_T **)objmethods.ga_data)[cl_i]->uf_name) 982
895 == 0) 983 pobj_method_offset += pclass->class_obj_method_count_child;
896 { 984 pclass = pclass->class_extends;
897 int *table = (int *)(if2cl + 1);
898 table[if_i] = cl_i;
899 done = TRUE;
900 break;
901 }
902 }
903
904 if (!done && extends_cl != NULL)
905 {
906 for (int cl_i = 0;
907 cl_i < extends_cl->class_obj_method_count; ++cl_i)
908 {
909 if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
910 extends_cl->class_obj_methods[cl_i]->uf_name)
911 == 0)
912 {
913 int *table = (int *)(if2cl + 1);
914 table[if_i] = objmethods.ga_len + cl_i;
915 break;
916 }
917 }
918 }
919 } 985 }
920 } 986 }
921 } 987 }
922 988
923 if (is_class && cl->class_class_member_count > 0) 989 if (is_class && cl->class_class_member_count > 0)