diff src/vim9class.c @ 31754:48431422f766 v9.0.1209

patch 9.0.1209: getting interface member does not always work Commit: https://github.com/vim/vim/commit/29ac5df37baf7e6e751c7ebd4ab37a2aa826e9e6 Author: Bram Moolenaar <Bram@vim.org> Date: Mon Jan 16 19:43:47 2023 +0000 patch 9.0.1209: getting interface member does not always work Problem: Getting interface member does not always work. Solution: Convert the index on the interface to the index on the object. (closes #11825)
author Bram Moolenaar <Bram@vim.org>
date Mon, 16 Jan 2023 20:45:04 +0100
parents d3d3ed2c09f6
children 64f03e860c91
line wrap: on
line diff
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -197,6 +197,32 @@ add_members_to_class(
 }
 
 /*
+ * Convert a member index "idx" of interface "itf" to the member index of class
+ * "cl" implementing that interface.
+ */
+    int
+object_index_from_itf_index(class_T *itf, int idx, class_T *cl)
+{
+    if (idx > itf->class_obj_member_count)
+    {
+	siemsg("index %d out of range for interface %s", idx, itf->class_name);
+	return 0;
+    }
+    itf2class_T *i2c;
+    for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
+	if (i2c->i2c_class == cl)
+	    break;
+    if (i2c == NULL)
+    {
+	siemsg("class %s not found on interface %s",
+					      cl->class_name, itf->class_name);
+	return 0;
+    }
+    int *table = (int *)(i2c + 1);
+    return table[idx];
+}
+
+/*
  * Handle ":class" and ":abstract class" up to ":endclass".
  * Handle ":interface" up to ":endinterface".
  */
@@ -765,22 +791,6 @@ early_ret:
 
 	cl->class_extends = extends_cl;
 
-	if (ga_impl.ga_len > 0)
-	{
-	    // Move the "implements" names into the class.
-	    cl->class_interface_count = ga_impl.ga_len;
-	    cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
-	    if (cl->class_interfaces == NULL)
-		goto cleanup;
-	    for (int i = 0; i < ga_impl.ga_len; ++i)
-		cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
-	    VIM_CLEAR(ga_impl.ga_data);
-	    ga_impl.ga_len = 0;
-
-	    cl->class_interfaces_cl = intf_classes;
-	    intf_classes = NULL;
-	}
-
 	// Add class and object members to "cl".
 	if (add_members_to_class(&classmembers,
 				 extends_cl == NULL ? NULL
@@ -798,6 +808,48 @@ early_ret:
 				 &cl->class_obj_member_count) == FAIL)
 	    goto cleanup;
 
+	if (ga_impl.ga_len > 0)
+	{
+	    // Move the "implements" names into the class.
+	    cl->class_interface_count = ga_impl.ga_len;
+	    cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
+	    if (cl->class_interfaces == NULL)
+		goto cleanup;
+	    for (int i = 0; i < ga_impl.ga_len; ++i)
+		cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
+	    VIM_CLEAR(ga_impl.ga_data);
+	    ga_impl.ga_len = 0;
+
+	    // For each interface add a lookuptable for the member index on the
+	    // interface to the member index in this class.
+	    for (int i = 0; i < cl->class_interface_count; ++i)
+	    {
+		class_T *ifcl = intf_classes[i];
+		itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
+				 + ifcl->class_obj_member_count * sizeof(int));
+		if (if2cl == NULL)
+		    goto cleanup;
+		if2cl->i2c_next = ifcl->class_itf2class;
+		ifcl->class_itf2class = if2cl;
+		if2cl->i2c_class = cl;
+
+		for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
+		    for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
+		    {
+			if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
+				     cl->class_obj_members[cl_i].ocm_name) == 0)
+			{
+			    int *table = (int *)(if2cl + 1);
+			    table[if_i] = cl_i;
+			    break;
+			}
+		    }
+	    }
+
+	    cl->class_interfaces_cl = intf_classes;
+	    intf_classes = NULL;
+	}
+
 	if (is_class && cl->class_class_member_count > 0)
 	{
 	    // Allocate a typval for each class member and initialize it.
@@ -1411,6 +1463,13 @@ class_unref(class_T *cl)
 	vim_free(cl->class_interfaces);
 	vim_free(cl->class_interfaces_cl);
 
+	itf2class_T *next;
+	for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next)
+	{
+	    next = i2c->i2c_next;
+	    vim_free(i2c);
+	}
+
 	for (int i = 0; i < cl->class_class_member_count; ++i)
 	{
 	    ocmember_T *m = &cl->class_class_members[i];