Mercurial > vim
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) |